DISTINCT für nur eine Spalte


155

Angenommen, ich habe die folgende Abfrage.

SELECT ID, Email, ProductName, ProductModel FROM Products

Wie kann ich es so ändern, dass keine doppelten E-Mails zurückgegeben werden?

Mit anderen Worten, wenn mehrere Zeilen dieselbe E-Mail enthalten, soll das Ergebnis nur eine dieser Zeilen enthalten (vorzugsweise die letzte). Duplikate in anderen Spalten sollten zulässig sein.

Klauseln wie DISTINCTund GROUP BYscheinen für ganze Zeilen zu funktionieren. Ich bin mir also nicht sicher, wie ich das angehen soll.


2
Ok, Sie müssen PARTITION oder zwei select-Anweisungen verwenden?
CarneyCode

Und was sollte angezeigt werden, wenn beispielsweise 2 Zeilen mit derselben E-Mail-Adresse, aber unterschiedlichem Produktnamen vorhanden sind? Das (vorzugsweise das letzte) ist nicht klar. Zuletzt bei welcher Bestellung?
Ypercubeᵀᴹ

@ypercube Wie in der Frage angegeben, vorzugsweise die letzte. Das ist mir jedoch nicht wirklich kritisch. Ich will nur einen von ihnen.
Jonathan Wood

1
Sie können sich die folgenden Fragen ansehen: Frage1 , Frage2 oder Frage3 .
Marian

Warum können Sie nicht verwenden: SELECT DISTINCT Email, ID, ProductName, ProductModel FROM Products?
Rick Henderson

Antworten:


186

Wenn Sie SQL Server 2005 oder höher verwenden, verwenden Sie Folgendes:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
              ) a
WHERE rn = 1

EDIT: Beispiel mit einer where-Klausel:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
                   WHERE ProductModel = 2
                     AND ProductName LIKE 'CYBER%'

              ) a
WHERE rn = 1

4
Ich muss diese PARTITION-Klausel untersuchen, habe sie noch nie in Aktion gesehen. Vielen Dank für das Beispiel
LorenVS

@Cybernate Eine Komplikation: Mein Inneres SELECTbraucht einen WHEREZustand. Ich denke, die Zeilennummern werden allen Zeilen in der Tabelle zugewiesen. Diese Syntax ist mir ein wenig unverständlich. Gibt es eine Chance auf ein Update, das eine Zeile mit einer bestimmten E-Mail garantiert, die die WHEREBedingung erfüllt?
Jonathan Wood

1
Sie können dem inneren SQL eine where-Klausel hinzufügen. Ich werde den Beitrag aktualisieren, sobald ich auf meinen Laptop zugreifen kann
Chandu

1
Der Beitrag wurde mit einem Beispiel unter Verwendung der where-Klausel aktualisiert.
Chandu

1
Ich bekomme das nur dann richtig zum Laufen, wenn meine Abfrage kein JOIN s enthält. Sobald ich a habe JOIN, ROW_NUMBERgibt das viel höhere Werte als "1" zurück.
Uwe Keim

10

Dies setzt SQL Server 2005+ voraus und Ihre Definition von "last" ist die maximale PK für eine bestimmte E-Mail

WITH CTE AS
(
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel, 
       ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC) AS RowNumber 
FROM   Products
)
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel
FROM CTE 
WHERE RowNumber = 1

6

Wenn Sie es verwenden DISTINCT, stellen Sie es sich als eine bestimmte Zeile vor, nicht als eine Spalte. Es werden nur Zeilen zurückgegeben, in denen die Spalten nicht genau übereinstimmen.

SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products

----------------------
1 | something@something.com | ProductName1 | ProductModel1
2 | something@something.com | ProductName1 | ProductModel1

Die Abfrage würde beide Zeilen zurückgeben, da die IDSpalte unterschiedlich ist. Ich gehe davon aus, dass die IDSpalte eine IDENTITYinkrementierende Spalte ist. Wenn Sie die letzte zurückgeben möchten, empfehle ich Folgendes:

SELECT DISTINCT TOP 1 ID, Email, ProductName, ProductModel
FROM Products
ORDER BY ID DESC

Der TOP 1gibt nur den ersten Datensatz zurück. Wenn Sie ihn nach IDabsteigend sortieren, werden die Ergebnisse mit der letzten Zeile zuerst zurückgegeben. Dies gibt Ihnen die letzte Aufzeichnung.


2
Wie in der Frage angegeben, funktioniert DISTINCT in der gesamten Zeile. Ich möchte tun, wie Sie oben vorgeschlagen haben, aber für jedes Mal wird die E-Mail in den Ergebnissen dupliziert (nicht nur einmal).
Jonathan Wood

In diesem Fall würde ich empfehlen, mit @Cybernate Antwort zu gehen. Das sollte genau das tun, was Sie brauchen.
Jon3laze

4

Sie können dies mit der GROUP BY-Funktion beheben

SELECT ID, Email, ProductName, ProductModel FROM Products GROUP BY Email


16
Die Spalte 'Products.ID' ist in der Auswahlliste ungültig, da sie weder in einer Aggregatfunktion noch in der GROUP BY-Klausel enthalten ist.
Palota

2
Dies funktioniert nicht ohne die Verwendung von MAX (ID), MAX (ProductName), MAX (ProductModel) für die anderen Spalten
avl_sweden

2
In postgres benötigen Sie nur die Aggregatfunktion für die Spalte, die in der group by-Klausel verwendet wird, z SELECT id, max(email) AS email FROM tbl GROUP by email. In SQL Server müssen sich ALLE Spalten in der SELECTKlausel in einer Aggregatfunktion befinden. Das beißt mich jedes Mal, wenn ich zurückgehe.
Bruce Pierson

Das wird niemals funktionieren. Es ist eine schlechte Lösung
Dan AS

1

Für Access können Sie die hier ausgewählte SQL Select-Abfrage verwenden:

Zum Beispiel haben Sie diese Tabelle:

KUNDE || NOMBREN || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

123 || JOHN CONNOR || s.connor@skynet.com

125 || SARAH CONNOR ||s.connor@skynet.com

Und Sie müssen nur bestimmte Mails auswählen. Sie können es damit machen:

SQL SELECT:

SELECT MAX(p.CLIENTE) AS ID_CLIENTE
, (SELECT TOP 1 x.NOMBRES 
    FROM Rep_Pre_Ene_MUESTRA AS x 
    WHERE x.MAIL=p.MAIL 
     AND x.CLIENTE=(SELECT MAX(l.CLIENTE) FROM Rep_Pre_Ene_MUESTRA AS l WHERE x.MAIL=l.MAIL)) AS NOMBRE, 
p.MAIL
FROM Rep_Pre_Ene_MUESTRA AS p
GROUP BY p.MAIL;

Sie können dies verwenden, um die maximale ID auszuwählen, den Korrespondenznamen zu dieser maximalen ID. Auf diese Weise können Sie jedes andere Attribut hinzufügen. Am Ende setzen Sie die zu filternde eindeutige Spalte und gruppieren sie nur mit dieser letzten eindeutigen Spalte.

Dadurch erhalten Sie die maximale ID mit den entsprechenden Daten. Sie können min oder andere Funktionen verwenden und diese Funktion in die Unterabfragen replizieren.

Diese Auswahl gibt Folgendes zurück:

KUNDE || NOMBREN || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

125 || SARAH CONNOR ||s.connor@skynet.com

Denken Sie daran, die ausgewählten Spalten zu indizieren, und die jeweilige Spalte darf nicht alle numerischen Daten in Groß- oder Kleinbuchstaben enthalten, da dies sonst nicht funktioniert. Dies funktioniert auch mit nur einer registrierten Mail. Viel Spaß beim Codieren !!!


0

Der Grund DISTINCTund die GROUP BYArbeit an ganzen Zeilen ist, dass Ihre Abfrage ganze Zeilen zurückgibt.

Zum besseren Verständnis: Versuchen Sie, von Hand zu schreiben, was die Abfrage zurückgeben soll, und Sie werden feststellen, dass es nicht eindeutig ist, was in die nicht duplizierten Spalten eingefügt werden soll.

Wenn es Ihnen buchstäblich egal ist, was in den anderen Spalten steht, geben Sie sie nicht zurück. Die Rückgabe einer zufälligen Zeile für jede E-Mail-Adresse erscheint mir etwas nutzlos.


@ JohnFix Ich möchte ganze Zeilen zurückgeben. Ich möchte nur nicht, dass Zeilen zurückgegeben werden, wenn die Ergebnisse bereits eine Zeile mit demselben Wert in der Spalte E-Mail enthalten.
Jonathan Wood

Wie sollte es also entscheiden, welches zurückgegeben werden soll? Möchten Sie wirklich eine Abfrage, die für jede E-Mail eine beliebige Zeile zurückgibt? Das riecht wirklich so, als müssten Sie das Problem, das Sie lösen möchten, möglicherweise überdenken. Fast jedes Mal, wenn mir diese Frage gestellt wurde (und sie taucht häufig auf), stellte sich heraus, dass der Entwickler die Konsequenzen in der App für dieses Verhalten nicht durchdacht hat.
JohnFx

6
Ich habe wirklich Probleme, deiner Logik zu folgen. Wie in der Frage angegeben, würde ich die letzte bevorzugen (sortiert nach ID). Ja, wenn eine zufällige Zeile ausgewählt würde, wäre das in Ordnung. Und ja, ich habe darüber nachgedacht.
Jonathan Wood

0

Versuche dies

;With Tab AS (SELECT DISTINCT Email FROM  Products)
SELECT Email,ROW_NUMBER() OVER(ORDER BY Email ASC) AS  Id FROM Tab
ORDER BY Email ASC

-2

Versuche dies:

SELECT ID, Email, ProductName, ProductModel FROM Products WHERE ID IN (SELECT MAX(ID) FROM Products GROUP BY Email)

2
Warum sollten wir das versuchen? Warum ist das besser als die anderen Antworten, die hier in den letzten 8 Jahren veröffentlicht wurden? Wenn Sie einen besseren Weg zur Lösung des Problems vorstellen möchten, müssen Sie erklären, warum Sie es empfehlen.
Dharman
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.