簡體   English   中英

用於在JSON數組中查找元素的索引

[英]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;

Postgres 9.3+中的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM