Index for finding an element in a JSON array
我有一个看起来像这样的表:
1 2 3 4 5 6 7 | 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是有原因的。
我要做的是查找具有特定艺术家姓名(完全匹配)的曲目。
我正在使用此查询:
1 2 3 | SELECT * FROM tracks WHERE 'ARTIST NAME' IN (SELECT value->>'name' FROM json_array_elements(artists)) |
例如
1 2 3 | SELECT * FROM tracks WHERE 'The Dirty Heads' IN (SELECT value->>'name' FROM json_array_elements(artists)) |
但是,这会进行全表扫描,并且速度不是很快。 我尝试使用函数
使用新的二进制JSON数据类型
1 2 | CREATE TABLE tracks (id serial, artists jsonb); CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists); |
无需转换数组的函数。这将支持查询:
1 | SELECT * FROM tracks WHERE artists @> '[{"name":"The Dirty Heads"}]'; |
或者您为索引使用更专业的非默认GIN运算符类
1 2 | CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (artists jsonb_path_ops); |
相同的查询。
目前
如果
注意JSON对象和基本类型之间的区别:
- 在PostgreSQL中使用json数组中的索引
1 2 3 4 | 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); |
查询:
1 | SELECT * FROM tracks WHERE artistnames ? 'The Dirty Heads'; |
或者(如果经常重复名称,效率更高):
1 2 | CREATE INDEX tracks_artistnames_gin_idx ON tracks USING gin (artistnames jsonb_path_ops); |
查询:
1 | SELECT * FROM tracks WHERE artistnames @> '"The Dirty Heads"'::jsonb; |
这应该与
1 2 3 | 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)'; |
创建此功能索引:
1 2 | CREATE INDEX tracks_artists_gin_idx ON tracks USING gin (json2arr(artists, 'name')); |
并使用这样的查询。
1 2 | SELECT * FROM tracks WHERE '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name')); |
更新了评论中的反馈。我们需要使用数组运算符来支持GIN索引。
在这种情况下,"包含"运算符
关于功能波动的说明
即使不是 strike>,您也可以声明您的函数
大多数
1 2 3 4 5 | 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%'; |
功能索引仅适用于