sql >> Databasteknik >  >> RDS >> PostgreSQL

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

jsonb i Postgres 9.4+

Den binära JSON-datatypen jsonb förbättrar till stor del indexalternativ. Du kan nu ha ett GIN-index på en jsonb array direkt:

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

Inget behov av en funktion för att konvertera arrayen. Detta skulle stödja en fråga:

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

@> är jsonb "innehåller" operator, som kan använda GIN-index. (Inte för json , endast jsonb !)

Eller du använder den mer specialiserade, icke-standardiserade GIN-operatörsklassen jsonb_path_ops för indexet:

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

Samma fråga.

För närvarande jsonb_path_ops stöder endast @> operatör. Men det är vanligtvis mycket mindre och snabbare. Det finns fler indexalternativ, detaljer i manualen .

Om kolumnen artists innehåller bara namn som visas i exemplet, det skulle vara mer effektivt att lagra bara värdena som JSON-text primitiver och den redundanta nyckeln kan vara kolumnnamnet.

Notera skillnaden mellan JSON-objekt och primitiva typer:

  • Använda index i json-arrayen 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);

Fråga:

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

? fungerar inte för objektets värden , bara nycklar och matriselement .

Eller:

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

Fråga:

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

Mer effektivt om namn är mycket duplicerade.

json i Postgres 9.3+

Detta bör fungera 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)';

Skapa detta funktionella index :

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

Och använd en fråga så här. Uttrycket i WHERE satsen måste matcha den i indexet:

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

Uppdaterad med feedback i kommentarer. Vi måste använda matrisoperatorer för att stödja GIN-index.
Operatorn "innehålls av" <@ i det här fallet.

Anmärkningar om funktionsvolatilitet

Du kan deklarera din funktion IMMUTABLE även om json_array_elements() är inte var det inte.
De flesta JSON funktioner brukade bara vara STABLE , inte IMMUTABLE . Det fanns en diskussion på hackerlistan för att ändra på det. De flesta är IMMUTABLE nu. Kolla 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%';

Funktionella index fungerar bara med IMMUTABLE funktioner.




  1. FEL:Fel 1005:Kan inte skapa tabell (errnr:121)

  2. MySQL – Olika metoder för att känna till nuvarande användare

  3. SQL är inte en gruppfunktion i en grupp

  4. Hur man får nya användare per dag i MySQL