PostgreSQLでは、IN
ですが、通常、妥当なリストの長さではかなり小さな違いがあります。 概念的にははるかにクリーンです。非常に長いAND ... <> ...
リストと非常に長いNOT IN
リストは両方ともAND
でひどく機能します NOT IN
よりもはるかに悪い 。
どちらの場合も、質問をするのに十分な長さである場合は、代わりに値リストに対して反結合またはサブクエリの除外テストを実行する必要があります。
WITH excluded(item) AS (
VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT *
FROM thetable t
WHERE NOT EXISTS(SELECT 1 FROM excluded e WHERE t.item = e.item);
または:
WITH excluded(item) AS (
VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT *
FROM thetable t
LEFT OUTER JOIN excluded e ON (t.item = e.item)
WHERE e.item IS NULL;
(最新のPgバージョンでは、どちらも同じクエリプランを生成します。)
値リストが十分に長い場合(数万のアイテム)、クエリの解析にかなりのコストがかかる可能性があります。この時点で、TEMPORARY
の作成を検討する必要があります テーブル、COPY
データを除外してデータにインデックスを作成し、CTEの代わりに一時テーブルで上記のアプローチのいずれかを使用します。
デモ:
CREATE UNLOGGED TABLE exclude_test(id integer primary key);
INSERT INTO exclude_test(id) SELECT generate_series(1,50000);
CREATE TABLE exclude AS SELECT x AS item FROM generate_series(1,40000,4) x;
ここでexclude
除外する値のリストです。
次に、同じデータに対する次のアプローチをミリ秒単位のすべての結果と比較します。
-
NOT IN
リスト: 3424.596 -
AND ...
リスト: 80173.823 VALUES
ベースのJOIN
除外: 20.727VALUES
ベースのサブクエリの除外: 20.495- テーブルベースの
JOIN
、元リストにインデックスがありません: 25.183 - サブクエリテーブルベース、ex-listにインデックスなし: 23.985
...CTEベースのアプローチをAND
よりも3000倍以上高速化 リストし、NOT IN
より130倍高速 リスト。
ここにコードを書いてください:https://gist.github.com/ringerc/5755247(このリンクをたどるあなたがたは目を保護してください)
このデータセットのサイズでは、除外リストにインデックスを追加しても違いはありませんでした。
注:
-
IN
SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
で生成されたリスト -
AND
SELECT string_agg(item::text, ' AND item <> ') from exclude;
で生成されたリスト ) - サブクエリと結合ベースのテーブルの除外は、繰り返し実行してもほとんど同じでした。
- 計画を検討すると、Pgは
NOT IN
を翻訳していることがわかります<> ALL
へ
だから...あなたは本当に巨大ながあることがわかります 両方のIN
間のギャップ およびAND
リストと適切な結合の実行。私が驚いたのは、VALUES
を使用してCTEでそれを実行する速度が速かったことです。 リストは...VALUES
を解析していました リストはほとんど時間がかからず、同じかわずかに速い ほとんどのテストでテーブルアプローチ。
PostgreSQLが途方もなく長いIN
を自動的に認識できればいいのですが 類似のAND
の句またはチェーン 条件を設定し、ハッシュ結合を実行したり、暗黙的にCTEノードに変換したりするなどのよりスマートなアプローチに切り替えます。今のところ、その方法がわかりません。
参照:
- この便利なブログ投稿MagnusHaganderがこのトピックについて書いています