Cara memilih hanya baris pertama untuk setiap nilai unik kolom

96

Misalkan saya memiliki tabel alamat pelanggan:

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

Di tabel, satu pelanggan seperti John Smith dapat memiliki beberapa alamat. Saya memerlukan kueri pemilihan untuk tabel ini untuk mengembalikan hanya baris pertama yang ditemukan di mana ada duplikat di 'CName'. Untuk tabel ini, ia harus mengembalikan semua baris kecuali yang ke-3 (atau ke-1 - salah satu dari dua alamat itu baik-baik saja tetapi hanya satu yang dapat dikembalikan). Apakah ada kata kunci yang dapat saya tambahkan ke kueri PILIH untuk memfilter berdasarkan apakah server telah melihat nilai kolom sebelumnya?

nuit9
sumber

Jawaban:

126

Jawaban yang sangat sederhana jika Anda mengatakan Anda tidak peduli alamat mana yang digunakan.

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

Jika Anda ingin yang pertama menurut, katakanlah, kolom "disisipkan" maka itu adalah kueri yang berbeda

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
sumber
Meskipun mungkin tidak dimaksudkan untuk digunakan dengan cara ini saat memilih 10 kolom. Juga tampaknya tidak dapat menerima kolom dari tipe bit.
nuit9
1
@ nuit9: tentu saja itu tidak akan bekerja dengan bit dan 10 kolom. Tak satu pun dari fakta ini yang menjadi pertanyaan Anda. Anda akan menggunakan teknik ke-2 atau teknik Ben Thul. Saya menjawab apa yang Anda tanyakan secara spesifik, dengan petunjuk tentang cara menyelesaikan secara lebih umum.
gbn
Bagian pertama DO bekerja dengan banyak kolom, meskipun tidak dengan kolom tipe bit. Saya menguji ini di MS SQL server 2016.
netfed
24

Di SQL 2k5 +, Anda dapat melakukan sesuatu seperti:

;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
sumber
5
Tolong jelaskan apa yang dimaksud dengan rank, partisi dan [r] lakukan
Roberto
10

Anda bisa menggunakan row_number()untuk mendapatkan nomor baris dari baris tersebut. Ini menggunakan overperintah - partition byklausa menentukan kapan harus memulai kembali penomoran dan order bymemilih apa yang harus dipesan nomor baris. Bahkan jika Anda menambahkan order bydi akhir kueri, itu akan mempertahankan urutan dalam overperintah saat penomoran.

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1
jujur
sumber
6
Di postgresql, fungsi jendela tidak diperbolehkan di klausa WHERE
ekanna
3
Ini tidak diperbolehkan untuk MS-SQL.
Mixxiphoid
1
ROW_NUMBER()tidak berfungsi dalam Whereklausul di Teradata juga
Pirate X
6

Anda dapat menggunakan row_numer() over(partition by ...)sintaks seperti ini:

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

Apa yang dilakukannya adalah membuat kolom bernama row, yang merupakan penghitung yang bertambah setiap kali melihat hal yang sama CName, dan mengindeks kejadian tersebut AddressLine. Dengan memaksakan where row = 1, seseorang dapat memilih CNamesiapa yang AddressLinedatang lebih dulu menurut abjad. Jika order byadalah desc, maka itu akan memilih CNamesiapa yang AddressLinedatang terakhir menurut abjad.

FatihAkici
sumber
1

Ini akan memberi Anda satu baris dari setiap baris duplikat. Ini juga akan memberi Anda kolom tipe bit, dan bekerja setidaknya di 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

Jika Anda ingin mencari semua duplikat, cukup ubah rn = 1 menjadi rn> 1. Semoga ini bisa membantu

netfed
sumber