sql >> Databasteknik >  >> RDS >> PostgreSQL

Postgres 9.4 jsonb-array som tabell

Fråga

Din tabelldefinition saknas. Förutsatt att:

CREATE TABLE configuration (
  config_id serial PRIMARY KEY
, config jsonb NOT NULL
);

För att hitta ett value och dess rad för given oid och instance :

SELECT c.config_id, d->>'value' AS value
FROM   configuration c
     , jsonb_array_elements(config->'data') d  -- default col name is "value"
WHERE  d->>'oid'      = '1.3.6.1.4.1.7352.3.10.2.5.35.3'
AND    d->>'instance' = '0'
AND    d->>'value'   <> '1'

Det är en implicit LATERAL Ansluta sig. Jämför:

  • Fråga efter arrayelement i JSON-typ

2) Vilket är det snabbaste sättet att få en tabell med 3 kolumner med oid , instance och value.

Jag antar att jag använder jsonb_populate_recordset() , då kan du ange datatyper i tabelldefinitionen. Förutsatt text för alla:

CREATE TEMP TABLE data_pattern (oid text, value text, instance text);

Kan också vara en beständig (icke-temp) tabell. Den här är endast för den aktuella sessionen. Sedan:

SELECT c.config_id, d.*
FROM   configuration c
     , jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d

Det är allt. Den första frågan skrevs om:

SELECT c.config_id, d.*
FROM   configuration c
     , jsonb_populate_recordset(NULL::data_pattern, c.config->'data') d
WHERE  d.oid      = '1.3.6.1.4.1.7352.3.10.2.5.35.3'
AND    d.instance = '0'
AND    d.value   <> '1';

Men det är långsammare än den första frågan. Nyckeln till prestanda med större tabell är indexstöd:

Index

Du kan enkelt indexera den normaliserade (översatta) tabellen eller den alternativa layout du föreslog i frågan. Indexerar din aktuella layout är inte lika självklart, men också möjligt. För bästa prestanda föreslår jag ett funktionsindex på bara data nyckel med jsonb_path_ops operatörsklass. Per dokumentation:

Den tekniska skillnaden mellan en jsonb_ops och en jsonb_path_ops GINindex är att den förra skapar oberoende indexposter för varje nyckel och värde i data, medan den senare skapar indexposter endast för varje värde i datan.

Detta bör göra underverk för prestanda:

CREATE INDEX configuration_my_idx ON configuration
USING gin ((config->'data') jsonb_path_ops);

Man kan förvänta sig att endast en fullständig matchning för ett JSON-arrayelement skulle fungera, som:

SELECT * FROM configuration
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
                            , "instance": "0", "value": "1234"}]';

Observera JSON-arraynotationen (med omslutande [] ) av det angivna värdet, som krävs.

Men matriselement med en underuppsättning av nycklar fungerar också:

SELECT * FROM configuration
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3"
                            , "instance": "0"}]'

Det svåra är att införliva ditt till synes misstänkta predikat value <> '1' . Försiktighet måste iakttas för att tillämpa alla predikat på samma arrayelement. Du kan kombinera detta med den första frågan:

SELECT c.*, d->>'value' AS value
FROM   configuration c
     , jsonb_array_elements(config->'data') d
WHERE  (config->'data') @> '[{"oid": "1.3.6.1.4.1.7352.3.10.2.5.35.3", "instance": "0"}]'
AND    d->>'oid'      = '1.3.6.1.4.1.7352.3.10.2.5.35.3'  -- must be repeated
AND    d->>'instance' = '0'                               -- must be repeated
AND    d->>'value'   <> '1'                               -- here we can rule out

Voilá.

Specialindex

Om ditt bord är stort kan indexstorleken vara en avgörande faktor. Du kan jämföra prestandan för denna speciallösning med ett funktionsindex:

Den här funktionen extraherar en Postgres-array av oid-instans kombinationer från en given jsonb värde:

CREATE OR REPLACE FUNCTION f_config_json2arr(_j jsonb)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
$func$
SELECT ARRAY(
   SELECT (elem->>'oid') || '-' || (elem->>'instance')
   FROM   jsonb_array_elements(_j) elem
   )
$func$

Vi kan bygga ett funktionsindex baserat på detta:

CREATE INDEX configuration_conrfig_special_idx ON configuration
USING  gin (f_config_json2arr(config->'data'));

Och basera frågan på det:

SELECT * FROM configuration
WHERE  f_config_json2arr(config->'data') @> '{1.3.6.1.4.1.7352.3.10.2.5.35.3-0}'::text[]

Tanken är att indexet ska vara betydligt mindre eftersom det bara lagrar de kombinerade värdena utan nycklar. matrisen inneslutningsoperatör @> själv bör fungera liknande jsonb-inneslutningsoperatören @> . Jag förväntar mig ingen stor skillnad, men jag skulle vara väldigt intresserad vilket är snabbare.

Liknar den första lösningen i detta relaterade svar (men mer specialiserat):

  • Index för att hitta ett element i en JSON-array

Förutom:

  • Jag skulle inte använda oid som kolumnnamn eftersom det också används för interna ändamål i Postgres.
  • Om möjligt skulle jag använda en vanlig, normaliserad tabell utan JSON.



  1. Använder OASIS-SVN och git för åtkomst till källkodskontroll

  2. Migrera en Oracle-databas till MySQL på AWS, del 1

  3. CURRENT_TIMESTAMP i millisekunder

  4. Varför är det säkrare att använda en mysql-förberedd sats än att använda de vanliga escape-funktionerna?