次のような表があります。
_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
"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
_は_@>
_演算子のみをサポートしています。しかし、通常ははるかに小さくて高速です。他のインデックスオプション、 マニュアルの詳細 があります。
Ifartists
が例に表示されている名前のみを保持している場合、冗長性の低いJSON値を最初から保存する方が効率的です。テキストとしてのvaluesprimitivesおよび冗長keyは列名に含めることができます。
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';
_
_?
_ はオブジェクトvaluesでは機能せず、ただkeysおよびarray elements。
または(名前が頻繁に繰り返される場合はより効率的です):
_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
function で動作するはずです:
_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)';
_
これを作成します 機能index :
_CREATE INDEX tracks_artists_gin_idx ON tracks
USING gin (json2arr(artists, 'name'));
_
そして、このようなqueryを使用します。 WHERE
句の式は、インデックスの式と一致する必要があります。
_SELECT * FROM tracks
WHERE '{"The Dirty Heads"}'::text[] <@ (json2arr(artists, 'name'));
_
コメントにフィードバックを追加して更新しました。 GINインデックスをサポートするには、 配列演算子 を使用する必要があります。
"is contains by"演算子_<@
_ この場合。
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
関数でのみ機能します。