PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Jaki jest właściwy indeks do odpytywania struktur w tablicach w jsonb Postgresa?

Przede wszystkim nie możesz uzyskać dostępu do wartości tablicy JSON w ten sposób. Dla danej wartości json

[{"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"}]

Prawidłowy test na pierwszy element tablicy to:

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

Ale prawdopodobnie nie chcesz ograniczać wyszukiwania do pierwszego elementu tablicy. Z jsonb typ danych w Postgresie 9.4 masz dodatkowe operatory i obsługę indeksów. Aby zindeksować elementy tablicy, potrzebujesz indeksu GIN.

Wbudowane klasy operatorów dla indeksów GIN nie obsługują operatorów „większy niż” ani „mniejszy niż” > >= < <= . Dotyczy to jsonb jak również, gdzie możesz wybrać pomiędzy dwiema klasami operatorów. Zgodnie z dokumentacją:

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

(jsonb_ops jest wartością domyślną). Możesz pokryć test równości, ale żaden z tych operatorów nie spełnia wymagań dla >= porównanie. Będziesz potrzebował indeksu btree.

Podstawowe rozwiązanie

Aby wesprzeć sprawdzanie równości za pomocą indeksu:

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

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

Może to wystarczyć, jeśli filtr jest wystarczająco selektywny.
Zakładając, że end_time >= start_time , więc nie potrzebujemy dwóch kontroli. Sprawdzanie tylko end_time jest tańszy i równoważny:

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;

Wykorzystanie niejawnego JOIN LATERAL . Szczegóły (ostatni rozdział):

  • PostgreSQL unnest() z numerem elementu

Uważaj na różne typy danych ! To, co masz w wartości JSON, wygląda jak timestamp [without time zone] , podczas gdy Twoje predykaty używają timestamp with time zone literały. timestamp wartość jest interpretowana zgodnie z aktualną strefą czasową ustawienie, podczas gdy podany timestamptz literały muszą być rzutowane na timestamptz jawnie lub strefa czasowa zostanie zignorowana! Powyższe zapytanie powinno działać zgodnie z oczekiwaniami. Szczegółowe wyjaśnienie:

  • Całkowite ignorowanie stref czasowych w Rails i PostgreSQL

Więcej wyjaśnień dla jsonb_array_elements() :

  • Dołączanie do PostgreSQL przy użyciu JSONB

Zaawansowane rozwiązanie

Jeśli powyższe nie jest wystarczająco dobre, rozważyłbym MATERIALIZED VIEW który przechowuje odpowiednie atrybuty w znormalizowanej formie. Pozwala to na zwykłe indeksy btree.

Kod zakłada, że ​​Twoje wartości JSON mają spójny format, jak pokazano w pytaniu.

Konfiguracja:

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;

Powiązana odpowiedź dla jsonb_populate_recordset() :

  • Jak przekonwertować typ jsonb PostgreSQL 9.4 na pływający
CREATE INDEX loc_event_idx ON loc_event (event_slug, end_time, location_id);

Uwzględnia także location_id aby zezwolić na skanowanie tylko do indeksu . (Zobacz stronę podręcznika i Postgres Wiki.)

Zapytanie:

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

Lub, jeśli potrzebujesz pełnych wierszy z podstawowych locations tabela:

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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak ustawić automatyczny przyrost klucza podstawowego w PostgreSQL?

  2. Utwórz bazę danych Postgres za pomocą Pythona

  3. Głowy w chmurze na CHAR(10)

  4. Django prefetch_związane z limitem

  5. Jak zoptymalizować replikację logiczną PostgreSQL