これまでの回答をまとめ、クリーンアップして改善すると、次の優れたクエリに到達します。
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
これは、それらのいずれよりもはるかに高速です。現在受け入れられている回答のパフォーマンスを 10 ~ 15 倍にします (PostgreSQL 8.4 および 9.1 での私のテストでは)。
しかし、これはまだ最適とは言えません。NOT EXISTS
パフォーマンスをさらに向上させるには、(アンチ) セミ結合を使用します。EXISTS
標準SQLであり、永遠に存在しており(少なくともPostgreSQL 7.2以降、この質問が尋ねられるずっと前から)、提示された要件に完全に適合しています。
UPDATE sales s
SET status = 'ACTIVE'
WHERE NOT EXISTS (
SELECT FROM sales s1 -- SELECT list can be empty for EXISTS
WHERE s.saleprice = s1.saleprice
AND s.saledate = s1.saledate
AND s.id <> s1.id -- except for row itself
)
AND s.status IS DISTINCT FROM 'ACTIVE'; -- avoid empty updates. see below
db<>fiddle here
古い SQL フィドル
行を識別する一意のキー
テーブルの主キーまたは一意のキーがない場合 (例)、このクエリの目的でid
システム列に置き換えることができます (ただし、他の目的ではできません)。ctid
AND s1.ctid <> s.ctid
すべてのテーブルには主キーが必要です。まだお持ちでない場合は追加してください。Postgres 10+serial
の列または列をお勧めします。IDENTITY
関連している:
これはどのように高速ですか?
EXISTS
反セミ結合のサブクエリは、最初の重複が見つかるとすぐに評価を停止できます (これ以上調べても意味がありません)。重複がほとんどないベーステーブルの場合、これはわずかに効率的です。重複が多いと、これはより効率的になります。
空の更新を除外する
この更新が既にある行の場合status = 'ACTIVE'
、何も変更されませんが、新しい行バージョンが全額で挿入されます (小さな例外が適用されます)。通常、これは必要ありません。WHERE
これを回避してさらに高速化するには、上記のような別の条件を追加します。
status
が定義されている場合NOT NULL
、次のように簡略化できます。
AND status <> 'ACTIVE';
<>
列のデータ型は、演算子をサポートしている必要があります。json
そうでないタイプもあります。見る:
NULL 処理の微妙な違い
このクエリは ( Joel による現在受け入れられている回答とは異なり) NULL 値を等しいものとして扱いません。次の 2 つの行は(saleprice, saledate)
、「別個」と見なされます (ただし、人間の目と同じように見えます)。
(123, NULL)
(123, NULL)
また、NULL 値は SQL 標準に従って同等に比較されないため、一意のインデックスと他のほとんどすべての場所を渡します。見る:
OTOH、GROUP BY
、DISTINCT
またはDISTINCT ON ()
NULL 値を等しいものとして扱います。達成したいことに応じて、適切なクエリ スタイルを使用します。一部またはすべての比較IS NOT DISTINCT FROM
の代わりに、このより高速なクエリを引き続き使用して、NULL 比較を等しくすることができます。=
もっと:
比較されるすべての列が定義されている場合NOT NULL
、不一致の余地はありません。