可変長配列を含むJSONBフィールドtable
を含むテーブルdata
があります。
{"label": "xyz", "items": [ ... ]}
"items"
要素の長さのインデックスを作成しました:
CREATE INDEX n_items ON table ( JSONB_ARRAY_LENGTH(data->'items') )
しかし、フィルターをかけた場合、フィルターをかけようとすると、引き続き順次スキャンが行われます。
EXPLAIN ANALYZE SELECT COUNT(*) FROM table WHERE JSONB_ARRAY_LENGTH(table.data->'items') = 2;
QUERY PLAN
-----------------------------------------------------------------------------------------
Aggregate (cost=2565655.67..2565655.68 rows=1 width=8)
-> Seq Scan on table (cost=0.00..2535256.19 rows=12159794 width=8)
Filter: (jsonb_array_length((table.data -> 'items'::text)) = 2)
Planning time: 0.121 ms
Execution time: 482891.694 ms
フィルター処理には約8分かかります。ここで何か間違ったことをしたのですか、それともPostgreSQLがJSON(B)オブジェクトの統計を保持しない結果ですか?このdata
列を平坦化することは可能ですが、作業を開始する前にそれが必要であることを確認したいと思います。
編集:これらの配列の長さはあまり変わりません。現在のところ、データには4つの異なる値しかありませんが、それ以上はないと思います。この場合、インデックスはあまり役に立ちませんか、それとも他の方法でフィルタリングを改善できますか?
あなたのデータを知らなければ、私はあなたのインデックスの選択性が低いと推測することができます(これは配列の長さがほとんど変わらない場合に起こります)。
これを克服するための1つのトリックは、クエリを少し変更して、カバーするインデックスを作成することです。このため、NOT NULL
列(たとえば、テーブルの主キー)をカウントし、この列をインデックスに含めます。
CREATE INDEX n_items ON your_table (jsonb_array_length(data->'items'), id);
SELECT count(id)
FROM your_table
WHERE JSONB_ARRAY_LENGTH(table.data->'items') = 2;
これはうまくいけばインデックスのみのスキャンになります(私はjsonb
の部分を省略してテストしましたが、機能するかどうかはわかります)。