[英]Index for finding an element in a JSON array
我有一個看起來像這樣的表:
CREATE TABLE tracks (id SERIAL, artists JSON);
INSERT INTO tracks (id, artists)
VALUES (1, '[{"name": "blink-182"}]');
INSERT INTO tracks (id, artists)
VALUES (2, '[{"name": "The Dirty Heads"}, {"name": "Louis Richards"}]');
還有其他幾個與此問題無關的列。 將它們存儲為JSON是有原因的。
我要做的是查找具有特定藝術家姓名 (完全匹配)的曲目。
我正在使用此查詢:
SELECT * FROM tracks
WHERE 'ARTIST NAME' IN
(SELECT value->>'name' FROM json_array_elements(artists))
例如
SELECT * FROM tracks
WHERE 'The Dirty Heads' IN
(SELECT value->>'name' FROM json_array_elements(artists))
但是,這會進行全表掃描,並且速度不是很快。 我嘗試使用函數names_as_array(artists)
創建GIN索引,並使用'ARTIST NAME' = ANY names_as_array(artists)
,但是不使用索引並且查詢實際上明顯更慢。
jsonb
Postgres里9.4+ 使用新的二進制JSON數據類型jsonb
,Postgres 9.4引入了很大程度上改進的索引選項 。 您現在可以直接在jsonb
數組上擁有GIN索引:
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
“包含”運算符 ,它可以使用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對象和基本類型之間的區別:
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
這應該與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索引。
在這種情況下,“包含”運算符<@
。
即使json_array_elements()
不是,
也可以聲明函數IMMUTABLE
。
大多數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
函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.