Cómo seleccionar solo las primeras filas para cada valor único de una columna

96

Digamos que tengo una tabla de direcciones de clientes:

CName           |   AddressLine
-------------------------------
John Smith      | 123 Nowheresville
Jane Doe        | 456 Evergreen Terrace
John Smith      | 999 Somewhereelse
Joe Bloggs      | 1 Second Ave

En la tabla, un cliente como John Smith puede tener varias direcciones. Necesito que la consulta de selección para esta tabla devuelva solo la primera fila encontrada donde hay duplicados en 'CName'. Para esta tabla, debe devolver todas las filas excepto la tercera (o la primera; cualquiera de esas dos direcciones está bien, pero solo se puede devolver una). ¿Hay una palabra clave que pueda agregar a la consulta SELECT para filtrar en función de si el servidor ya ha visto el valor de la columna antes?

nuit9
fuente

Respuestas:

125

Una respuesta muy simple si dice que no le importa qué dirección se use.

SELECT
    CName, MIN(AddressLine)
FROM
    MyTable
GROUP BY
    CName

Si desea la primera de acuerdo con, digamos, una columna "insertada", entonces es una consulta diferente

SELECT
    M.CName, M.AddressLine,
FROM
    (
    SELECT
        CName, MIN(Inserted) AS First
    FROM
        MyTable
    GROUP BY
        CName
    ) foo
    JOIN
    MyTable M ON foo.CName = M.CName AND foo.First = M.Inserted
gbn
fuente
Aunque puede que no esté diseñado para usarse de esta manera al seleccionar 10 columnas. También parece que no puede aceptar una columna del tipo bit.
nuit9
1
@ nuit9: por supuesto que no funcionará con bit y 10 columnas. Ninguno de estos hechos está en tu pregunta. Usaría la segunda técnica o la técnica de Ben Thul. Respondí lo que preguntaste específicamente, con sugerencias sobre cómo resolver de manera más general.
gbn
La primera parte SÍ funciona con varias columnas, aunque no con columnas de tipo bit. Sin embargo, probé esto en MS SQL Server 2016.
netfed
24

En SQL 2k5 +, puede hacer algo como:

;with cte as (
  select CName, AddressLine,
  rank() over (partition by CName order by AddressLine) as [r]
  from MyTable
)
select CName, AddressLine
from cte
where [r] = 1
Ben Thul
fuente
5
Por favor, explique qué hace el rango, la partición y [r]
Roberto
10

Puede usar row_number()para obtener el número de fila de la fila. Utiliza el overcomando: la partition bycláusula especifica cuándo reiniciar la numeración y order byselecciona en qué ordenar el número de fila. Incluso si agrega un order byal final de su consulta, conservará el orden en el overcomando al numerar.

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1
Franco
fuente
6
En postgresql, las funciones de ventana no están permitidas en la cláusula WHERE
ekanna
3
Esto tampoco está permitido para MS-SQL.
Mixxiphoid
1
ROW_NUMBER()no funciona en Wherecláusula en Teradata también
Pirate X
6

Puedes usar la row_numer() over(partition by ...)sintaxis así:

select * from
(
select *
, ROW_NUMBER() OVER(PARTITION BY CName ORDER BY AddressLine) AS row
from myTable
) as a
where row = 1

Lo que hace esto es que crea una columna llamada row, que es un contador que aumenta cada vez que ve lo mismo CName, e indexa esas ocurrencias por AddressLine. Al imponer where row = 1, uno puede seleccionar CNamequién AddressLineviene primero alfabéticamente. Si order byera desc, entonces elegiría CNamequién AddressLineviene en último lugar alfabéticamente.

FatihAkici
fuente
1

Esto le dará una fila de cada fila duplicada. También le dará las columnas de tipo bit, y funciona al menos en MS Sql Server.

(select cname, address 
from (
  select cname,address, rn=row_number() over (partition by cname order by cname) 
  from customeraddresses  
) x 
where rn = 1) order by cname

Si desea encontrar todos los duplicados, simplemente cambie rn = 1 a rn> 1. Espero que esto ayude

netfed
fuente