sql >> Database >  >> RDS >> PostgreSQL

Index voor het vinden van een element in een JSON-array

jsonb in Postgres 9.4+

Het binaire JSON-gegevenstype jsonb verbetert de indexopties grotendeels. U kunt nu een GIN-index hebben op een jsonb direct array:

CREATE TABLE tracks (id serial, artists jsonb);  -- !
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);

Er is geen functie nodig om de array te converteren. Dit zou een vraag ondersteunen:

SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';

@> zijnde de jsonb "bevat" operator, die de GIN-index kan gebruiken. (Niet voor json , alleen jsonb !)

Of u gebruikt de meer gespecialiseerde, niet-standaard GIN-operatorklasse jsonb_path_ops voor de index:

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (artists jsonb_path_ops);  -- !

Zelfde vraag.

Momenteel jsonb_path_ops ondersteunt alleen de @> exploitant. Maar het is meestal veel kleiner en sneller. Er zijn meer indexopties, details in de handleiding .

Als de kolom artists bevat alleen namen zoals weergegeven in het voorbeeld, het zou efficiënter zijn om alleen de waarden op te slaan als JSON-tekst primitieven en de overbodige sleutel kan de kolomnaam zijn.

Let op het verschil tussen JSON-objecten en primitieve typen:

  • Indexen gebruiken in json-array in PostgreSQL
CREATE TABLE tracks (id serial, artistnames jsonb);
INSERT INTO tracks  VALUES (2, '["The Dirty Heads", "Louis Richards"]');

CREATE INDEX tracks_artistnames_gin_idx ON tracks USING gin (artistnames);

Vraag:

SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';

? werkt niet voor object waarden , gewoon toetsen en array-elementen .

Of:

CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING  gin (artistnames jsonb_path_ops);

Vraag:

SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;

Efficiënter als namen zeer dubbel zijn.

json in Postgres 9.3+

Dit zou moeten werken met een IMMUTABLE functie :

CREATE OR REPLACE FUNCTION json2arr(_j json, _key text)
  RETURNS text[] LANGUAGE sql IMMUTABLE AS
'SELECT ARRAY(SELECT elem->>_key FROM json_array_elements(_j) elem)';

Maak deze functionele index :

CREATE INDEX tracks_artists_gin_idx ON tracks
USING  gin (json2arr(artists, 'name'));

En gebruik een query zoals dit. De uitdrukking in de WHERE clausule moet overeenkomen met die in de index:

SELECT * FROM tracks
WHERE  '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));

Bijgewerkt met feedback in opmerkingen. We moeten matrixoperatoren . gebruiken ter ondersteuning van de GIN-index.
De operator "is bevat door" <@ in dit geval.

Opmerkingen over functievolatiliteit

U kunt uw functie IMMUTABLE . declareren zelfs als json_array_elements() is niet was niet.
De meeste JSON functies waren voorheen alleen STABLE , niet IMMUTABLE . Er was een discussie over de hackerslijst om daar verandering in te brengen. De meeste zijn IMMUTABLE nu. Controleer met:

SELECT p.proname, p.provolatile
FROM   pg_proc p
JOIN   pg_namespace n ON n.oid = p.pronamespace
WHERE  n.nspname = 'pg_catalog'
AND    p.proname ~~* '%json%';

Functionele indexen werken alleen met IMMUTABLE functies.




  1. PostgreSQL waar alles in array staat

  2. Hoe maak je een logbestand aan in Oracle met PL/SQL?

  3. INSTR() Functie in Oracle

  4. Een inleiding tot tijdreeksdatabases