Wie wähle ich (oder kann ich) DISTINCT für mehrere Spalten aus?
-
09-06-2019 - |
Frage
Ich muss alle Zeilen aus einer Tabelle abrufen, in der zwei Spalten zusammengenommen alle unterschiedlich sind.Ich möchte also alle Verkäufe, für die es keine anderen Verkäufe gibt, die am selben Tag zum gleichen Preis stattgefunden haben.Die auf der Grundlage von Tag und Preis eindeutigen Verkäufe werden auf einen aktiven Status aktualisiert.
Also ich denke:
UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
FROM sales
HAVING count = 1)
Aber mein Gehirn schmerzt, wenn ich darüber hinausgehe.
Lösung
SELECT DISTINCT a,b,c FROM t
ist ungefähr entspricht:
SELECT a,b,c FROM t GROUP BY a,b,c
Es ist eine gute Idee, um sich an den GROUP BY-Syntax verwendet, da es leistungsstärker ist.
Für Ihre Abfrage, ich es so tun würde:
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
)
Andere Tipps
Wenn Sie die Antworten zusammen so weit, aufzuräumen und zu verbessern, Sie bei dieser überlegenen Abfrage ankommen würde:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Welche ist viel schneller als jeder von ihnen. Nukes die Leistung der derzeit akzeptierte Antwort um den Faktor 10 bis 15 (in meinen Tests auf PostgreSQL 8.4 und 9.1)
. Das ist aber bei weitem noch nicht optimal. Verwenden Sie ein NOT EXISTS
(Anti-) Semi-Join für eine noch bessere Leistung. EXISTS
ist Standard-SQL, gibt es schon immer (zumindest seit PostgreSQL 7.2, lange bevor diese Frage wurde gebeten) und passt die dargestellten Anforderungen perfekt:
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 <> Geige hier
Old SQL Fiddle
Eindeutiger Schlüssel zu identifizieren Zeile
Wenn Sie nicht mit einer primären oder eindeutigen Schlüssel für die Tabelle (id
im Beispiel) haben, können Sie mit der Systemspalte ctid
für die Zwecke dieser Abfrage ersetzen (aber nicht für einige andere Zwecke):
AND s1.ctid <> s.ctid
Jede Tabelle sollte einen Primärschlüssel haben. Fügen Sie ein, wenn Sie keine hat, noch nicht. Ich schlage vor, eine serial
oder eine IDENTITY
Spalte in Postgres 10 +.
Siehe auch:
Wie ist das schneller?
Die Unterabfrage in dem EXISTS
anti-semi beitreten kann, sobald die erste Betrogene gefunden ist (keinen Punkt in der Suche weiter) stoppen zu bewerten. Für eine Basistabelle mit wenigen Duplikaten ist dies nur leicht effizienter. Mit vielen Duplikaten dieser wird Weg effizienter zu gestalten.
ausschließen leer Updates
Für Zeilen, die bereits dieses Update status = 'ACTIVE'
haben würde nichts ändern, aber immer noch eine neue Zeile Version zu Vollkosten einfügen (kleinere Ausnahmen gelten). Normalerweise Sie dies nicht wollen. Ein weiteren hinzufügen WHERE
Zustand wie gezeigt oben, dies zu vermeiden und macht es noch schneller:
Wenn status
NOT NULL
definiert ist, können Sie vereinfachen zu:
AND status <> 'ACTIVE';
Subtle Unterschied in NULL Handhabung
Diese Abfrage (im Gegensatz zu dem zur Zeit Antwort von Joel akzeptiert) behandelt nicht NULL-Wert als gleich. Die folgenden zwei Zeilen für (saleprice, saledate)
würden qualifizieren als „distinct“ (obwohl die Suche identisch mit dem menschlichen Auge):
(123, NULL)
(123, NULL)
geht auch in einem eindeutigen Index und fast überall sonst, da NULL-Werte entsprechend den SQL-Standard nicht gleich vergleichen. Siehe auch:
OTOH, GROUP BY
, DISTINCT
oder DISTINCT ON ()
behandeln NULL-Werte als gleich. Verwenden Sie eine entsprechende Abfrage Stil je nachdem, was Sie erreichen wollen. Sie können immer noch diese schnellere Abfrage mit IS NOT DISTINCT FROM
statt =
für einige oder alle vergleiche machen gleich NULL vergleichen. Mehr:
Wenn alle Spaltenverglichenen NOT NULL
definiert sind, gibt es keinen Raum für Meinungsverschiedenheiten.
Das Problem mit Ihrer Anfrage ist, dass, wenn ein GROUP BY-Klausel (die Sie im Wesentlichen durch die Verwendung verschieden tun) Sie nur Spalten verwenden können, die Sie Gruppe durch oder Aggregatfunktionen. Sie können die Spalte-ID verwenden, da es möglicherweise unterschiedliche Werte sind. In Ihrem Fall ist es immer nur ein Wert wegen der HAVING-Klausel, aber die meisten RDBMS sind nicht intelligent genug, um zu erkennen, dass.
Dies soll jedoch arbeiten (und nicht einen Join müssen):
UPDATE sales
SET status='ACTIVE'
WHERE id IN (
SELECT MIN(id) FROM sales
GROUP BY saleprice, saledate
HAVING COUNT(id) = 1
)
Sie können auch anstelle von MIN verwenden MAX oder AVG, ist es nur wichtig ist, eine Funktion zu verwenden, die den Wert der Spalte zurückgibt, wenn es nur eine passende Zeile ist.
Ich mag die unterschiedlichen Werte von einer Spalte ‚GrondOfLucht‘ wählen, aber sie sollten in der Reihenfolge sortiert werden, wie in der Spalte gegeben ‚sortering‘. Ich kann nicht die unterschiedlichen Werte von nur einer Spalte erhalten mit
Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering
Es wird auch die Spalte ‚sortering‘ geben und weil ‚GrondOfLucht‘ UND ‚Sortering‘ ist nicht eindeutig, wird das Ergebnis alle Zeilen.
Bei der Gruppe verwenden, um die Aufzeichnungen von ‚GrondOfLucht‘ in der Reihenfolge von ‚sortering
gegeben wählenSELECT GrondOfLucht
FROM dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)
Wenn Ihr DBMS nicht eindeutige Unterstützung mit mehreren Spalten wie folgt aus:
select distinct(col1, col2) from table
Multi wählen Sie im Allgemeinen sicher wie folgt ausgeführt werden:
select distinct * from (select col1, col2 from table ) as x
Da es sich bei den meisten der DBMS arbeiten können und dies wird voraussichtlich schneller als Gruppe von Lösung, wie Sie die Gruppierung Funktionalität zu vermeiden.