DISTINCT per una sola colonna


155

Diciamo che ho la seguente domanda.

SELECT ID, Email, ProductName, ProductModel FROM Products

Come posso modificarlo in modo che non restituisca e-mail duplicate?

In altre parole, quando più righe contengono la stessa e-mail, voglio che i risultati includano solo una di quelle righe (preferibilmente l'ultima). I duplicati in altre colonne dovrebbero essere consentiti.

Le clausole gradiscono DISTINCTe GROUP BYsembrano funzionare su intere righe. Quindi non sono sicuro di come affrontare questo.


2
Ok, devi usare PARTITION o usare due istruzioni select?
Carney,

E cosa dovrebbe essere mostrato se sono presenti 2 righe con la stessa e-mail ma con un nome prodotto diverso? Il (preferibilmente l'ultimo) non è chiaro. Ultimo con quale ordine?
ypercubeᵀᴹ

@ypercube Come indicato nella domanda, preferibilmente l'ultimo. Tuttavia, questo non è molto critico per me. Ne voglio solo uno.
Jonathan Wood,

1
Puoi guardare le seguenti domande: domanda 1 , domanda 2 o domanda 3 .
Marian,

Perché non puoi usare: SELEZIONA DISTINCT Email, ID, ProductName, ProductModel FROM Products?
Rick Henderson,

Risposte:


186

Se si utilizza SQL Server 2005 o versione successiva, utilizzare questo:

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: esempio usando una clausola where:

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
Devo indagare su questa clausola di PARTITION, mai vista prima in azione. Grazie per l'esempio
LorenVS,

@Cybernate Una complicazione: il mio interno ha SELECTbisogno di una WHEREcondizione. Sto pensando che i numeri delle righe verranno assegnati a tutte le righe della tabella. Questa sintassi è solo un po 'oltre me. Qualche possibilità di un aggiornamento che garantirebbe una riga con una determinata email che soddisfa la WHEREcondizione?
Jonathan Wood,

1
È possibile aggiungere la clausola where al sql interno. Aggiornerò il post non appena potrò accedere al mio laptop
Chandu il

1
Aggiornato il post con un esempio usando la clausola where.
Chandu,

1
Ottengo che funzioni correttamente solo quando non JOIN ho messaggi nella mia query. Non appena ho un JOIN, ROW_NUMBERrestituisce valori molto più alti di "1".
Uwe Keim,

10

Ciò presuppone che SQL Server 2005+ e la definizione di "ultimo" sia il PK massimo per una determinata email

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

Quando lo usi DISTINCTpensa a una riga distinta, non a una colonna. Restituirà solo le righe in cui le colonne non corrispondono esattamente allo stesso.

SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products

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

La query restituirebbe entrambe le righe perché la IDcolonna è diversa. Suppongo che la IDcolonna sia una IDENTITYcolonna che sta aumentando, se si desidera restituire l'ultima, allora consiglio una cosa del genere:

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

L' TOP 1restituirà solo il primo record, ordinando esso dal IDdiscendente restituisca i risultati con l'ultima riga prima. Questo ti darà l'ultimo record.


2
Come indicato nella domanda, vedo che DISTINCT funziona su tutta la riga. Voglio fare come suggerisci sopra, ma per ogni volta che l'email viene duplicata nei risultati (non solo una volta).
Jonathan Wood,

In tal caso, consiglierei di andare con la risposta @Cybernate. Questo dovrebbe fare esattamente ciò di cui hai bisogno.
jon3laze,

4

Puoi farlo usando la funzione GROUP BY

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


16
La colonna "Products.ID" non è valida nell'elenco di selezione perché non è contenuta né in una funzione aggregata né nella clausola GROUP BY.
palota,

2
Questo non funziona senza usare qualcosa come MAX (ID), MAX (ProductName), MAX (ProductModel) per le altre colonne
avl_sweden

2
In postgres, hai solo bisogno della funzione aggregata sulla colonna che verrà utilizzata nella clausola group by, ad es SELECT id, max(email) AS email FROM tbl GROUP by email. Nel server SQL TUTTE le colonne nella SELECTclausola devono essere in una funzione aggregata. Questo mi morde ogni volta che torno.
Bruce Pierson,

Questo non funzionerà mai. È una pessima soluzione
Dan AS

1

Per Access, è possibile utilizzare la query SQL Select che presento qui:

Ad esempio hai questa tabella:

CLIENTE || NOMBRES || MAIL

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

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

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

E devi selezionare solo mail distinte. Puoi farlo con questo:

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;

Puoi usarlo per selezionare l'ID massimo, il nome corrispondente a quell'ID massimo, puoi aggiungere qualsiasi altro attributo in quel modo. Quindi alla fine metti la colonna distinta da filtrare e la raggruppi solo con l'ultima colonna distinta.

Questo ti porterà l'ID massimo con i dati corrispondenti, puoi usare min o qualsiasi altra funzione e replicherai quella funzione nelle sottoquery.

Questa selezione restituirà:

CLIENTE || NOMBRES || MAIL

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

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

Ricorda di indicizzare le colonne selezionate e la colonna distinta non deve contenere tutti i dati numerici in maiuscolo o minuscolo, altrimenti non funzionerà. Funzionerà anche con una sola posta registrata. Buona codifica !!!


0

Il motivo DISTINCTe il GROUP BYlavoro su intere righe è che la query restituisce intere righe.

Per aiutarti a capire: prova a scrivere a mano ciò che la query dovrebbe restituire e vedrai che è ambiguo cosa inserire nelle colonne non duplicate.

Se non ti interessa letteralmente cosa c'è nelle altre colonne, non restituirle. Restituire una riga casuale per ogni indirizzo e-mail mi sembra un po 'inutile.


@JohnFix Voglio restituire intere righe. Non voglio che le righe vengano restituite quando i risultati includono già una riga con lo stesso valore nella colonna Email.
Jonathan Wood,

Quindi, come dovrebbe decidere quale restituire? Desideri davvero una query che restituisca una riga arbitraria per ogni e-mail. Questo puzza davvero che potresti aver bisogno di ripensare il problema che stai cercando di risolvere. Quasi ogni volta che mi viene posta questa domanda (e si presenta molto) si scopre che lo sviluppatore non ha riflettuto sulle conseguenze nell'app per questo comportamento.
JohnFx,

6
Ho davvero problemi a seguire la tua logica. Come indicato nella domanda, preferirei l'ultimo (ordinato per ID). Sì, se selezionasse una riga casuale andrebbe bene. E sì, ci ho pensato.
Jonathan Wood,

0

Prova questo

;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

Prova questo:

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

2
Perché dovremmo provare questo? Perché è meglio delle altre risposte postate qui negli ultimi 8 anni? Se si desidera condividere un modo migliore per risolvere il problema, è necessario spiegare perché lo si consiglia.
Dharman,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.