sql >> Databasteknik >  >> RDS >> PostgreSQL

Vad är det korrekta indexet för att fråga strukturer i arrayer i Postgres jsonb?

Först och främst kan du inte komma åt JSON-arrayvärden som det. För ett givet json-värde

[{"event_slug":"test_1","start_time":"2014-10-08","end_time":"2014-10-12"},
 {"event_slug":"test_2","start_time":"2013-06-24","end_time":"2013-07-02"},
 {"event_slug":"test_3","start_time":"2014-03-26","end_time":"2014-03-30"}]

Ett giltigt test mot det första arrayelementet skulle vara:

WHERE e->0->>'event_slug' = 'test_1'

Men du vill förmodligen inte begränsa din sökning till det första elementet i arrayen. Med jsonb datatyp i Postgres 9.4 har du ytterligare operatörer och indexstöd. För att indexera element i en array behöver du ett GIN-index.

De inbyggda operatorklasserna för GIN-index stöder inte operatorerna "större än" eller "mindre än" > >= < <= . Detta gäller för jsonb också, där du kan välja mellan två operatörsklasser. Per dokumentation:

Name             Indexed Data Type  Indexable Operators
...
jsonb_ops        jsonb              ? ?& ?| @>
jsonb_path_ops   jsonb              @>
   

(jsonb_ops är standard.) Du kan täcka likhetstestet, men ingen av dessa operatörer täcker ditt krav på >= jämförelse. Du skulle behöva ett btree-index.

Grundlösning

För att stödja jämställdhetskontrollen med ett index:

CREATE INDEX locations_events_gin_idx ON locations
USING gin (events jsonb_path_ops);

SELECT * FROM locations WHERE events @> '[{"event_slug":"test_1"}]';

Detta kan vara tillräckligt bra om filtret är tillräckligt selektivt.
Antar end_time >= start_time , så vi behöver inte två kontroller. Kontrollerar endast end_time är billigare och likvärdig:

SELECT l.*
FROM   locations l
     , jsonb_array_elements(l.events) e
WHERE  l.events @> '[{"event_slug":"test_1"}]'
AND   (e->>'end_time')::timestamp >= '2014-10-30 14:04:06 -0400'::timestamptz;

Använder en implicit JOIN LATERAL . Detaljer (sista kapitlet):

  • PostgreSQL unnest() med elementnummer

Var försiktig med de olika datatyperna ! Det du har i JSON-värdet ser ut som timestamp [without time zone] , medan dina predikat använder timestamp with time zone bokstavliga ord. timestamp värdet tolkas enligt den aktuella tidszonen inställning, medan den givna timestamptz bokstaver måste castas till timestamptz explicit, annars skulle tidszonen ignoreras! Ovanstående fråga bör fungera som önskat. Detaljerad förklaring:

  • Ignorerar tidszoner helt och hållet i Rails och PostgreSQL

Mer förklaring för jsonb_array_elements() :

  • PostgreSQL-anslutning med JSONB

Avancerad lösning

Om ovanstående inte är tillräckligt bra skulle jag överväga en MATERIALIZED VIEW som lagrar relevanta attribut i normaliserad form. Detta tillåter vanliga btree-index.

Koden förutsätter att dina JSON-värden har ett konsekvent format som visas i frågan.

Inställningar:

CREATE TYPE event_type AS (
 , event_slug  text
 , start_time  timestamp
 , end_time    timestamp
);

CREATE MATERIALIZED VIEW loc_event AS
SELECT l.location_id, e.event_slug, e.end_time  -- start_time not needed
FROM   locations l, jsonb_populate_recordset(null::event_type, l.events) e;

Relaterat svar för jsonb_populate_recordset() :

  • Hur man konverterar PostgreSQL 9.4:s jsonb-typ till flytande
CREATE INDEX loc_event_idx ON loc_event (event_slug, end_time, location_id);

Inkluderar även location_id för att tillåta endast indexsökningar . (Se manualsidan och Postgres Wiki.)

Fråga:

SELECT *
FROM   loc_event
WHERE  event_slug = 'test_1'
AND    end_time  >= '2014-10-30 14:04:06 -0400'::timestamptz;

Eller om du behöver hela rader från de underliggande locations tabell:

SELECT l.*
FROM  (
   SELECT DISTINCT location_id
   FROM   loc_event
   WHERE  event_slug = 'test_1'
   AND    end_time  >= '2014-10-30 14:04:06 -0400'::timestamptz
   ) le
JOIN locations l USING (location_id);


  1. Hur Access 2019 fungerar och hur du arbetar med det

  2. Ta bort med Gå med i MySQL

  3. Oracle får kontrollsummavärde för en databit som definieras av en select-sats

  4. Hur man lägger till linjenummer i SQL Server Management Studio ( SSMS) - SQL Server / TSQL självstudie del 11