DISTINCT para solo una columna

156

Digamos que tengo la siguiente consulta.

SELECT ID, Email, ProductName, ProductModel FROM Products

¿Cómo puedo modificarlo para que no devuelva correos electrónicos duplicados?

En otras palabras, cuando varias filas contienen el mismo correo electrónico, quiero que los resultados incluyan solo una de esas filas (preferiblemente la última). Se deben permitir duplicados en otras columnas.

Las cláusulas tienen gusto DISTINCTy GROUP BYparecen funcionar en filas enteras. Así que no estoy seguro de cómo abordar esto.

Jonathan Wood
fuente
2
Ok, ¿necesitas usar PARTITION o usar dos declaraciones select?
CarneyCode
¿Y qué se debe mostrar si hay 2 filas con el mismo correo electrónico pero diferente ProductName? El (preferiblemente el último) no está claro. ¿Último por qué orden?
ypercubeᵀᴹ
@ypercube Como se indica en la pregunta, preferiblemente la última. Sin embargo, eso no es realmente crítico para mí. Solo quiero uno de ellos.
Jonathan Wood
1
Puede consultar las siguientes preguntas: pregunta1 , pregunta2 o pregunta3 .
Marian
¿Por qué no puede usar: SELECCIONE DISTINCT Correo electrónico, ID, ProductName, ProductModel FROM Products?
Rick Henderson

Respuestas:

186

Si está utilizando SQL Server 2005 o superior, use esto:

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

EDITAR: Ejemplo usando una cláusula 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
Chandu
fuente
44
Debo investigar esta cláusula de PARTICIÓN, nunca la había visto en acción antes. Gracias por el ejemplo
LorenVS
@Cybernate Una complicación: mi interior SELECTnecesita una WHEREcondición. Estoy pensando que los números de fila se asignarán a todas las filas de la tabla. Esta sintaxis está un poco más allá de mí. ¿Alguna posibilidad de una actualización que garantice una fila con un correo electrónico en particular que cumpla con la WHEREcondición?
Jonathan Wood
1
Puede agregar la cláusula where al sql interno. Actualizaré la publicación una vez que pueda acceder a mi computadora portátil
Chandu
1
Se actualizó la publicación con una muestra utilizando la cláusula where.
Chandu
1
Esto funciona correctamente solo cuando no tengo JOINs en mi consulta. Tan pronto como tengo un JOIN, el ROW_NUMBERdevuelve valores mucho más altos que "1".
Uwe Keim
10

Esto asume SQL Server 2005+ y su definición de "último" es la PK máxima para un correo electrónico dado

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
Pero P.
fuente
6

Cuando lo use, DISTINCTpiense en él como una fila distinta, no como una columna. Solo devolverá filas donde las columnas no coincidan exactamente igual.

SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products

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

La consulta devolvería ambas filas porque la IDcolumna es diferente. Supongo que la IDcolumna es una IDENTITYcolumna que se está incrementando, si desea devolver la última, le recomiendo algo como esto:

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

El TOP 1devolverá sólo el primer registro, ordenando que el IDdescendente que devolverá los resultados de la última fila en primer lugar. Esto te dará el último registro.

jon3laze
fuente
2
Como se indica en la pregunta, veo que DISTINCT funciona en toda la fila. Quiero hacer lo que sugieres anteriormente, pero cada vez que el correo electrónico se duplica en los resultados (no solo una vez).
Jonathan Wood
En ese caso, recomendaría ir con la respuesta @Cybernate. Eso debería hacer exactamente lo que necesitas.
jon3laze
4

Puede superar eso usando la función GROUP BY

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

Marshall Unduemi
fuente
16
La columna 'Productos.ID' no es válida en la lista de selección porque no está contenida ni en una función agregada ni en la cláusula GROUP BY.
palota
2
Esto no funciona sin usar algo como MAX (ID), MAX (ProductName), MAX (ProductModel) para las otras columnas
avl_sweden
2
En postgres, solo necesita la función de agregado en la columna que se usará en el grupo por cláusula, por ejemplo SELECT id, max(email) AS email FROM tbl GROUP by email. En el servidor SQL, TODAS las columnas de la SELECTcláusula deben estar en una función agregada. Esto me muerde cada vez que regreso.
Bruce Pierson
Esto nunca funcionará. Es una mala solución
Dan AS
1

Para Access, puede usar la consulta SQL Select que presento aquí:

Por ejemplo, tienes esta tabla:

CLIENTE || NOMBRES || CORREO

888 || T800 ARNOLD || [email protected]

123 || JOHN CONNOR || [email protected]

125 || SARAH CONNOR ||[email protected]

Y solo debe seleccionar correos distintos. Puedes hacerlo con esto:

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;

Puede usar esto para seleccionar la ID máxima, el nombre correspondiente a esa ID máxima, puede agregar cualquier otro atributo de esa manera. Luego, al final, coloca la columna distinta para filtrar y solo la agrupa con esa última columna distinta.

Esto le proporcionará la ID máxima con los datos correspondientes, puede usar min o cualquier otra función y replica esa función en las subconsultas.

Esta selección devolverá:

CLIENTE || NOMBRES || CORREO

888 || T800 ARNOLD || [email protected]

125 || SARAH CONNOR ||[email protected]

Recuerde indexar las columnas que seleccione y la columna distinta no debe tener datos numéricos en mayúsculas o minúsculas, de lo contrario no funcionará. Esto también funcionará con un solo correo registrado. Feliz codificación !!!

jRam90
fuente
0

La razón DISTINCTy el GROUP BYtrabajo en filas enteras es que su consulta devuelve filas enteras.

Para ayudarlo a comprender: Intente escribir a mano lo que debería devolver la consulta y verá que es ambiguo qué poner en las columnas no duplicadas.

Si literalmente no le importa lo que hay en las otras columnas, no las devuelva. Devolver una fila aleatoria para cada dirección de correo electrónico me parece un poco inútil.

JohnFx
fuente
@ JohnFix Quiero devolver filas enteras. Simplemente no quiero que se devuelvan filas cuando los resultados ya incluyen una fila con el mismo valor en la columna Correo electrónico.
Jonathan Wood
Entonces, ¿cómo debería decidir cuál volver? ¿Realmente desea una consulta que devuelva una fila arbitraria para cada correo electrónico? Esto realmente huele a que podría necesitar repensar el problema que está tratando de resolver. Casi cada vez que me han hecho esta pregunta (y surge mucho) resulta que el desarrollador no ha pensado en las consecuencias en la aplicación para este comportamiento.
JohnFx
66
Realmente tengo problemas para seguir tu lógica. Como se indica en la pregunta, preferiría el último (ordenado por ID). Sí, si seleccionara una fila aleatoria, estaría bien. Y sí, lo he pensado.
Jonathan Wood
0

Prueba esto

;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
Abdullah Yousuf
fuente
-2

Prueba esto:

SELECT ID, Email, ProductName, ProductModel FROM Products WHERE ID IN (SELECT MAX(ID) FROM Products GROUP BY Email)
Сергей Пустовит
fuente
2
¿Por qué deberíamos intentar esto? ¿Por qué es esto mejor que las otras respuestas publicadas aquí en los últimos 8 años? Si desea compartir una mejor manera de resolver el problema, debe explicar por qué lo recomienda.
Dharman