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(このリンクをたどるあなたがたは目を保護してください)
このデータセットのサイズでは、除外リストにインデックスを追加しても違いはありませんでした。
注:
-
INSELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;で生成されたリスト -
ANDSELECT string_agg(item::text, ' AND item <> ') from exclude;で生成されたリスト ) - サブクエリと結合ベースのテーブルの除外は、繰り返し実行してもほとんど同じでした。
- 計画を検討すると、Pgは
NOT INを翻訳していることがわかります<> ALLへ
だから...あなたは本当に巨大ながあることがわかります 両方のIN間のギャップ およびAND リストと適切な結合の実行。私が驚いたのは、VALUESを使用してCTEでそれを実行する速度が速かったことです。 リストは...VALUESを解析していました リストはほとんど時間がかからず、同じかわずかに速い ほとんどのテストでテーブルアプローチ。
PostgreSQLが途方もなく長いINを自動的に認識できればいいのですが 類似のANDの句またはチェーン 条件を設定し、ハッシュ結合を実行したり、暗黙的にCTEノードに変換したりするなどのよりスマートなアプローチに切り替えます。今のところ、その方法がわかりません。
参照:
- この便利なブログ投稿MagnusHaganderがこのトピックについて書いています