以下はテーブル定義です(簡略化):
CREATE TABLE documents (
document_id int4 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
data_block jsonb NULL
);
サンプル値:
INSERT INTO documents (document_id, data_block)
VALUES
(878979,
'{"COMMONS": {"DATE": {"value": "2017-03-11"}},
"PAYABLE_INVOICE_LINES": [
{"AMOUNT": {"value": 52408.53}},
{"AMOUNT": {"value": 654.23}}
]}')
, (977656,
'{"COMMONS": {"DATE": {"value": "2018-03-11"}},
"PAYABLE_INVOICE_LINES": [
{"AMOUNT": {"value": 555.10}}
]}');
'PAYABLE_INVOICE_LINES'
要素の1つに1000.00より大きい'value'
が含まれているすべてのドキュメントを検索したい。
私のクエリは
select *
from documents d
cross join lateral jsonb_array_elements(d.data_block -> 'PAYABLE_INVOICE_LINES') as pil
where (pil->'AMOUNT'->>'value')::decimal > 1000
ただし、ドキュメントを50件に制限したいので、document_id
でグループ化し、結果を50件に制限する必要があります。
何百万ものドキュメントがある場合、このクエリは非常にコストが高く、100万で10秒かかります。
Jsonbオブジェクトの配列にGINインデックスを追加してみます。しかし、@>
のようなjsonb演算子を使用しているときにのみ適用されるようです。
パフォーマンスを向上させるためのアイデアはありますか?
これは一般に最適化が困難です。この種のテストでは、jsonb
に対する直接の演算子またはインデックスのサポートはありません。
EXISTS
は、少なくとも複数の配列要素が一致する行の重複と、結果の追加の(冗長な)列pil
を避けながら、少なくとも既存のものより高速である必要があります。
SELECT *
FROM documents d
WHERE EXISTS (
SELECT FROM jsonb_array_elements(d.data_block -> 'PAYABLE_INVOICE_LINES') pil
WHERE (pil->'AMOUNT'->>'value')::decimal > 1000
);
関連:
これを桁違いに高速化するには、行ごとの最大値を抽出し、冗長に保存するか、 IMMUTABLE
関数を非常に小さく高速な(ただし特殊化された)式インデックスで使用します。
CREATE OR REPLACE FUNCTION f_doc_max_amout(jsonb)
RETURNS numeric AS
$func$
SELECT max((a->'AMOUNT'->>'value')::numeric)
FROM jsonb_array_elements($1) a
$func$ LANGUAGE sql IMMUTABLE;
CREATE INDEX documents_max_amount_idx
ON documents (f_doc_max_amout(data_block -> 'PAYABLE_INVOICE_LINES'));
クエリ(インデックス式と一致する必要があります):
SELECT *
FROM documents d
WHERE f_doc_max_amout(data_block -> 'PAYABLE_INVOICE_LINES') > 1000;
dbfiddle ここ