sql >> Database teknologi >  >> RDS >> PostgreSQL

Postgres 9.4 jsonb-array som tabel

Forespørgsel

Din tabeldefinition mangler. Forudsat:

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

For at finde en value og dens række for givet oid og 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 er en implicit LATERAL tilslutte. Sammenlign:

  • Forespørgsel efter array-elementer inde i JSON-typen

2) Hvad er den hurtigste måde at få en tabel med 3 kolonner af oid , instance og value.

Jeg formoder at bruge jsonb_populate_recordset() , så kan du angive datatyper i tabeldefinitionen. Forudsat text for alle:

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

Kan også være et vedvarende (ikke-temp) bord. Denne er kun til den aktuelle session. Så:

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

Det er alt. Den første forespørgsel omskrevet:

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 er langsommere end den første forespørgsel. Nøglen til ydeevne med større tabel er indeksstøtte:

Indeks

Du kan nemt indeksere den normaliserede (oversatte) tabel eller det alternative layout, du foreslog i spørgsmålet. Indeksering af dit aktuelle layout er ikke så indlysende, men også muligt. For den bedste ydeevne foreslår jeg et funktionelt indeks på kun data tast med jsonb_path_ops operatørklasse. Per dokumentation:

Den tekniske forskel mellem en jsonb_ops og en jsonb_path_ops GINindex er, at førstnævnte opretter uafhængige indekselementer for hver nøgle og værdi i dataene, mens sidstnævnte kun opretter indekselementer for hver værdi i dataene.

Dette skulle gøre underværker for ydeevne:

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

Man kunne forvente, at kun et komplet match for et JSON-array-element ville fungere, 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"}]';

Bemærk JSON-array-notationen (med omsluttende [] ) af den angivne værdi, som er påkrævet.

Men array-elementer med et undersæt af nøgler arbejde også:

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

Den svære del er at inkorporere dit tilsyneladende mistænkelige tilføjede prædikat value <> '1' . Man skal sørge for at anvende alle prædikater på samme array element. Du kan kombinere dette med den første forespørgsel:

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á.

Særligt indeks

Hvis dit bord er enormt, kan indeksstørrelsen være en afgørende faktor. Du kan sammenligne ydeevnen af ​​denne specielle løsning med et funktionelt indeks:

Denne funktion udtrækker en Postgres-array af oid-instance kombinationer fra en given jsonb værdi:

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 bygge et funktionelt indeks ud fra dette:

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

Og baser forespørgslen 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 er, at indekset skal være væsentligt mindre, fordi det kun gemmer de kombinerede værdier uden nøgler. arrayet indeslutningsoperatør @> i sig selv skal fungere på samme måde som jsonb-indeslutningsoperatøren @> . Jeg forventer ikke den store forskel, men jeg ville være meget interesseret, hvilket er hurtigere.

Svarende til den første løsning i dette relaterede svar (men mere specialiseret):

  • Indeks til at finde et element i et JSON-array

Udover:

  • Jeg ville ikke bruge oid som kolonnenavn, da det også bruges til interne formål i Postgres.
  • Hvis det er muligt, ville jeg bruge en almindelig, normaliseret tabel uden JSON.



  1. Hvordan forespørger man indlejrede arrays i en postgres json-kolonne?

  2. Batch-tilstand normalisering og ydeevne

  3. Få rekordtællinger for alle tabeller i MySQL-databasen

  4. Sådan viser du den aktuelle indstilling for Null-output i PostgreSQL (psql)