jsonb
в Postgres 9.4+
Двоичният тип данни JSON jsonb
до голяма степен подобрява опциите за индекси. Вече можете да имате GIN индекс на jsonb
масив директно:
CREATE TABLE tracks (id serial, artists jsonb); -- !
CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists);
Няма нужда от функция за преобразуване на масива. Това ще поддържа заявка:
SELECT * FROM tracks WHERE artists @> '[{"name": "The Dirty Heads"}]';
@>
е jsonb
"contains" оператор, който може да използва GIN индекса. (Не за json
, само jsonb
!)
Или използвате по-специализирания клас на оператор GIN, който не е по подразбиране jsonb_path_ops
за индекса:
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (artists jsonb_path_ops); -- !
Същата заявка.
В момента jsonb_path_ops
поддържа само @>
оператор. Но обикновено е много по-малък и по-бърз. Има още опции за индекси, подробности в ръководството .
Ако колоната artists
съдържа само имена, както е показано в примера, би било по-ефективно да се съхраняват само стойностите като JSON текстови примитиви и излишния ключ може да бъде името на колоната.
Обърнете внимание на разликата между JSON обекти и примитивни типове:
- Използване на индекси в json масив в 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);
Запитване:
SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads';
?
не работи за стойности на обекти , само ключове и елементи на масив .
Или:
CREATE INDEX tracks_artistnames_gin_idx ON tracks
USING gin (artistnames jsonb_path_ops);
Запитване:
SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb;
По-ефективно, ако имената са силно дублиращи се.
json
в Postgres 9.3+
Това трябва да работи с IMMUTABLE
функция :
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)';
Създайте този функционален индекс :
CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (json2arr(artists, 'name'));
И използвайте заявка като този. Изразът в WHERE
клаузата трябва да съвпада с тази в индекса:
SELECT * FROM tracks
WHERE '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));
Актуализирано с обратна връзка в коментарите. Трябва да използваме оператори за масив за поддържане на индекса GIN.
Операторът "се съдържа от" <@
в този случай.
Бележки относно променливостта на функцията
Можете да декларирате вашата функция IMMUTABLE
дори ако json_array_elements()
не е не беше.
Повечето JSON
функциите са били само STABLE
, а не IMMUTABLE
. Имаше дискусия в списъка с хакери, за да се промени това. Повечето са IMMUTABLE
сега. Проверете с:
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%';
Функционалните индекси работят само с IMMUTABLE
функции.