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

Indeks til at finde et element i et JSON-array

jsonb i Postgres 9.4+

Den binære JSON-datatype jsonb forbedrer i høj grad indeksmuligheder. Du kan nu have et GIN-indeks på en jsonb array direkte:

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

Der er ikke behov for en funktion til at konvertere arrayet. Dette ville understøtte en forespørgsel:

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

@> er jsonb "indeholder" operator, som kan bruge GIN-indekset. (Ikke til json , kun jsonb !)

Eller du bruger den mere specialiserede, ikke-standard GIN-operatørklasse jsonb_path_ops for indekset:

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

Samme forespørgsel.

I øjeblikket jsonb_path_ops understøtter kun @> operatør. Men det er typisk meget mindre og hurtigere. Der er flere indeksmuligheder, detaljer i manualen .

Hvis kolonnen artists indeholder kun navne som vist i eksemplet, det ville være mere effektivt kun at gemme værdierne som JSON-tekst primitiver og den redundante nøgle kan være kolonnenavnet.

Bemærk forskellen mellem JSON-objekter og primitive typer:

  • Brug af indekser i json-array i 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);

Forespørgsel:

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

? virker ikke for objektets værdier , kun nøgler og array-elementer .

Eller:

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

Forespørgsel:

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

Mere effektivt, hvis navne er meget duplikative.

json i Postgres 9.3+

Dette burde fungere med en IMMUTABLE funktion :

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)';

Opret dette funktionelle indeks :

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

Og brug en forespørgsel sådan her. Udtrykket i WHERE klausul skal matche den i indekset:

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

Opdateret med feedback i kommentarer. Vi skal bruge array-operatorer for at understøtte GIN-indekset.
Operatøren "er indeholdt af" <@ i dette tilfælde.

Bemærkninger om funktionsvolatilitet

Du kan erklære din funktion IMMUTABLE selvom json_array_elements() er det ikke var det ikke.
De fleste JSON funktioner plejede kun at være STABLE , ikke IMMUTABLE . Der var en diskussion på hackerlisten for at ændre det. De fleste er IMMUTABLE nu. Tjek med:

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%';

Funktionelle indekser virker kun med IMMUTABLE funktioner.




  1. Sådan overvåger du databaseforekomsters tilstand

  2. Indsætter kun en række, hvis den ikke allerede er der

  3. MySQL datoformat snydeark

  4. Sådan finder du sorteringen i SQL Server (T-SQL)