Come selezionare solo le prime righe per ogni valore univoco di una colonna


96

Diciamo che ho una tabella di indirizzi dei clienti:

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

Nella tabella, un cliente come John Smith può avere più indirizzi. Ho bisogno che la query di selezione per questa tabella restituisca solo la prima riga trovata in cui sono presenti duplicati in "CName". Per questa tabella dovrebbe restituire tutte le righe tranne la terza (o la prima: uno qualsiasi di questi due indirizzi va bene ma solo uno può essere restituito). Esiste una parola chiave che posso aggiungere alla query SELECT per filtrare in base al fatto che il server abbia già visto il valore della colonna in precedenza?

Risposte:


125

Una risposta molto semplice se dici che non ti interessa quale indirizzo viene utilizzato.

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

Se vuoi il primo secondo, diciamo, una colonna "inserita", allora è una query diversa

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

Anche se potrebbe non essere inteso per essere utilizzato in questo modo quando si selezionano 10 colonne. Inoltre sembra che non possa accettare una colonna del tipo di bit.
nuit9

1
@ nuit9: ovviamente non funzionerà con bit e 10 colonne. Nessuno di questi fatti è nella tua domanda. Useresti la seconda tecnica o la tecnica di Ben Thul. Ho risposto a ciò che hai chiesto specificamente, con indicazioni su come risolvere più in generale.
gbn

La prima parte funziona con più colonne, anche se non con colonne di tipo bit. Tuttavia, l'ho provato in MS SQL Server 2016.
netfed l'

24

In SQL 2k5 +, puoi fare qualcosa come:

;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

5
Per favore, spiega cosa fanno rank, partition e [r]
Roberto

10

È possibile utilizzare row_number()per ottenere il numero di riga della riga. Utilizza il overcomando: la partition byclausola specifica quando riavviare la numerazione e order byseleziona su cosa ordinare il numero di riga. Anche se hai aggiunto un order byalla fine della tua query, manterrebbe l'ordine nel overcomando durante la numerazione.

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1

6
In postgresql, le funzioni finestra non sono consentite nella clausola WHERE
ekanna

3
Ciò non è consentito per MS-SQL.
Mixxiphoid

1
ROW_NUMBER()non funziona anche nella Whereclausola a Teradata
Pirate X

6

Puoi usare la row_numer() over(partition by ...)sintassi in questo modo:

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

Ciò che fa è che crea una colonna chiamata row, che è un contatore che aumenta ogni volta che vede lo stesso CNamee indicizza quelle occorrenze di AddressLine. Imponendo where row = 1, si può selezionare il CNamecui AddressLineprimo ordine alfabetico. Se order byera desc, sceglierebbe in ordine alfabetico il CNamecui AddressLinearriva per ultimo.


1

Questo ti darà una riga di ogni riga duplicata. Ti darà anche le colonne di tipo bit e funziona almeno in 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

Se invece vuoi trovare tutti i duplicati, cambia semplicemente rn = 1 in rn> 1. Spero che questo aiuti

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.