複数の列で SELECT DISTINCT を実行するにはどうすればよいですか (または実行できますか)
-
09-06-2019 - |
質問
結合された 2 つの列がすべて異なるテーブルからすべての行を取得する必要があります。したがって、同じ日に発生した他のセールのないすべてのセールを同じ価格で欲しいと考えています。日と価格に基づいた固有のセールがアクティブなステータスに更新されます。
そこで私はこう考えています:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
しかし、それ以上進むと脳が痛くなります。
解決
SELECT DISTINCT a,b,c FROM t
は だいたい に相当:
SELECT a,b,c FROM t GROUP BY a,b,c
GROUP BY 構文はより強力であるため、この構文に慣れることをお勧めします。
あなたのクエリに対して、私なら次のようにします。
UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
SELECT id
FROM sales S
INNER JOIN
(
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(*) = 1
) T
ON S.saleprice=T.saleprice AND s.saledate=T.saledate
)
他のヒント
これまでの答えをまとめ、整理し、改善すると、次のような優れたクエリに到達します。
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<>フィドル ここ
古い SQL フィドル
行を識別するための一意のキー
テーブルの主キーまたは一意キーがない場合 (id
例では)、システム列に置き換えることができます ctid
このクエリの目的のため (ただし、他の目的のためではありません):
AND s1.ctid <> s.ctid
すべてのテーブルには主キーが必要です。まだ持っていない場合は追加します。私が提案するのは、 serial
または IDENTITY
Postgres 10以降の列。
関連している:
これはどのように速いのでしょうか?
のサブクエリ EXISTS
anti-semi-join は、最初の重複が見つかるとすぐに評価を停止できます (これ以上探しても意味がありません)。重複がほとんどないベース テーブルの場合、これはわずかに効率が向上するだけです。重複がたくさんあるとこうなります 方法 もっと効率的。
空の更新を除外する
すでに存在する行の場合、 status = 'ACTIVE'
この更新では何も変更されませんが、全額コストで新しい行バージョンが挿入されます (マイナーな例外が適用されます)。通常、これは望ましくありません。別のを追加 WHERE
これを回避してさらに高速化するには、上記のような条件を使用します。
もし status
定義されています NOT NULL
, 、次のように簡略化できます。
AND status <> 'ACTIVE';
NULL処理の微妙な違い
このクエリは( ジョエルによる現在受け入れられている回答) は NULL 値を等しいものとして扱いません。次の 2 行は、 (saleprice, saledate)
(人間の目には同一に見えますが) 「独特」と見なされます。
(123, NULL)
(123, NULL)
また、SQL 標準に従って NULL 値は等しいと比較されないため、一意のインデックスやその他のほとんどの場所に渡されます。見る:
オトー、 GROUP BY
, DISTINCT
または DISTINCT ON ()
NULL 値を等しいものとして扱います。達成したい内容に応じて、適切なクエリ スタイルを使用してください。この高速なクエリは引き続き使用できます。 IS NOT DISTINCT FROM
の代わりに =
一部またはすべての比較で NULL 比較を等しくします。もっと:
比較されるすべての列が定義されている場合 NOT NULL
, 、異論の余地はありません。
クエリの問題は、GROUP BY 句を使用する場合 (基本的には、distinct を使用して実行します)、グループ化した列または集計関数しか使用できないことです。異なる値が存在する可能性があるため、列 ID は使用できません。あなたの場合、HAVING 句があるため値は常に 1 つだけですが、ほとんどの RDBMS はそれを認識できるほど賢くありません。
ただし、これは機能するはずです (結合は必要ありません)。
UPDATE sales
SET status='ACTIVE'
WHERE id IN (
SELECT MIN(id) FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(id) = 1
)
MIN の代わりに MAX または AVG を使用することもできます。一致する行が 1 つしかない場合に列の値を返す関数を使用することのみが重要です。
1 つの列「GrondOfLucht」から個別の値を選択したいのですが、それらは「sortering」列で指定された順序でソートする必要があります。を使用して 1 つの列だけの個別の値を取得できません
Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering
また、列「sortering」も指定されますが、「GrondOfLucht」と「sortering」は一意ではないため、結果はすべての行になります。
GROUP を使用して、「sortering」で指定された順序で「GrondOfLucht」のレコードを選択します。
SELECT GrondOfLucht
FROM dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)
DBMS が次のような複数の列の個別をサポートしていない場合:
select distinct(col1, col2) from table
複数選択は通常、次のように安全に実行できます。
select distinct * from (select col1, col2 from table ) as x
これはほとんどの DBMS で動作し、グループ化機能を回避するため、ソリューションによるグループ化よりも高速であることが期待されます。