Jak wybrać tylko pierwsze wiersze dla każdej unikalnej wartości kolumny


97

Powiedzmy, że mam tabelę adresów klientów:

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

W tabeli jeden klient, taki jak John Smith, może mieć wiele adresów. Potrzebuję zapytania wybierającego dla tej tabeli, aby zwracał tylko pierwszy znaleziony wiersz, w którym znajdują się duplikaty w „CName”. W przypadku tej tabeli powinna zwrócić wszystkie wiersze z wyjątkiem trzeciego (lub pierwszego - każdy z tych dwóch adresów jest prawidłowy, ale można zwrócić tylko jeden). Czy istnieje słowo kluczowe, które mogę dodać do zapytania SELECT, aby filtrować na podstawie tego, czy serwer widział już wcześniej wartość kolumny?

Odpowiedzi:


127

Bardzo prosta odpowiedź, jeśli powiesz, że nie obchodzi Cię, który adres jest używany.

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

Jeśli chcesz, aby pierwsza była zgodna, powiedzmy, z kolumną „wstawioną”, to jest to inne zapytanie

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

Chociaż może nie być przeznaczone do używania w ten sposób podczas wybierania 10 kolumn. Wydaje się również, że nie może zaakceptować kolumny typu bitowego.
nuit9

1
@ nuit9: oczywiście nie będzie działać z bitami i 10 kolumnami. Żaden z tych faktów nie jest przedmiotem twojego pytania. Użyłbyś drugiej techniki lub techniki Bena Thula. Odpowiedziałem na to, o co prosiłeś, ze wskazówkami, jak rozwiązać bardziej ogólnie.
gbn

Pierwsza część DZIAŁA z wieloma kolumnami, chociaż nie z kolumnami bitowymi. Przetestowałem to jednak w MS SQL Server 2016.
netfed

27

W SQL 2k5 + możesz zrobić coś takiego:

;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

6
Proszę wyjaśnić, co robi ranga, podział i [r]
Roberto

10

Możesz użyć, row_number()aby uzyskać numer wiersza wiersza. Używa overpolecenia - partition byklauzula określa, kiedy ponownie uruchomić numerowanie, a order bywybiera, na czym ma być uporządkowany numer wiersza. Nawet jeśli dodasz order byna końcu zapytania, zachowałoby to kolejność w overpoleceniu podczas numerowania.

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

6
W postgresql funkcje okna nie są dozwolone w klauzuli WHERE
ekanna

3
Nie jest to dozwolone w przypadku MS-SQL.
Mixxiphoid

1
ROW_NUMBER()nie działa również w Whereklauzuli w Teradata
Pirate X

6

Możesz użyć takiej row_numer() over(partition by ...)składni:

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

Powoduje to utworzenie kolumny o nazwie row, która jest licznikiem, który zwiększa się za każdym razem, gdy widzi to samo CName, i indeksuje te wystąpienia według AddressLine. Narzucając where row = 1, można wybrać, CNamekto AddressLinejest pierwszy w kolejności alfabetycznej. Gdyby tak order bybyło desc, wybierałoby to, CNamektóre AddressLinejest ostatnie w kolejności alfabetycznej.


1

To da ci jeden wiersz z każdego zduplikowanego wiersza. Podaje również kolumny typu bitowego i działa przynajmniej na serwerze MS Sql.

(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

Jeśli zamiast tego chcesz znaleźć wszystkie duplikaty, po prostu zmień rn = 1 na rn> 1. Mam nadzieję, że to pomoże

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.