Como selecionar apenas as primeiras linhas para cada valor único de uma coluna

96

Digamos que eu tenha uma tabela de endereços de clientes:

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

Na tabela, um cliente como John Smith pode ter vários endereços. Preciso que a consulta selecionada para esta tabela retorne apenas a primeira linha encontrada onde há duplicatas em 'CName'. Para esta tabela, ele deve retornar todas as linhas, exceto a 3ª (ou 1ª - qualquer um desses dois endereços está correto, mas apenas um pode ser retornado). Existe uma palavra-chave que posso adicionar à consulta SELECT para filtrar com base no fato de o servidor já ter visto o valor da coluna antes?

nuit9
fonte

Respostas:

125

Uma resposta muito simples se você disser que não se importa com o endereço usado.

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

Se você quiser o primeiro de acordo com, digamos, uma coluna "inserida", então é uma 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
fonte
Embora não deva ser usado dessa forma ao selecionar 10 colunas. Também parece que não pode aceitar uma coluna do tipo bit.
nuit9 de
1
@ nuit9: é claro que não funcionará com colunas de 10 bits. Nenhum desses fatos está em sua dúvida. Você usaria a 2ª técnica ou a técnica de Ben Thul. Eu respondi o que você perguntou especificamente, com dicas sobre como resolver de forma mais geral.
gbn
A primeira parte FUNCIONA com colunas múltiplas, embora não com colunas do tipo bit. Eu testei isso no MS SQL server 2016 embora.
alimentado em
24

No SQL 2k5 +, você pode fazer 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
fonte
5
Explique o que classifica, particiona e [r] faz
Roberto
10

Você pode usar row_number()para obter o número da linha da linha. Ele usa o overcomando - a partition bycláusula especifica quando reiniciar a numeração e order byseleciona em que ordem o número da linha. Mesmo se você adicionar um order byao final de sua consulta, isso preservará a ordem do overcomando durante a numeração.

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1
Frank
fonte
6
No postgresql, as funções de janela não são permitidas na cláusula WHERE
ekanna
3
Isso não é permitido para MS-SQL.
Mixxiphoid de
1
ROW_NUMBER()também não funciona na Wherecláusula do Teradata
Pirate X
6

Você pode usar a row_numer() over(partition by ...)sintaxe da seguinte forma:

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

O que isso faz é criar uma coluna chamada row, que é um contador que aumenta sempre que vê o mesmo CName, e indexa essas ocorrências por AddressLine. Ao impor where row = 1, pode-se selecionar o CNameque AddressLinevem primeiro em ordem alfabética. Se o order byfoi desc, em seguida, ele iria pegar o CNamecuja AddressLinevem por último em ordem alfabética.

FatihAkici
fonte
1

Isso lhe dará uma linha de cada linha duplicada. Ele também fornecerá as colunas do tipo bit e funciona pelo menos no 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 você quiser encontrar todas as duplicatas, basta alterar o rn = 1 para rn> 1. Espero que isso ajude

rede
fonte