Cách chỉ chọn các hàng đầu tiên cho mỗi giá trị duy nhất của một cột


96

Giả sử tôi có một bảng địa chỉ khách hàng:

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

Trong bảng, một khách hàng như John Smith có thể có nhiều địa chỉ. Tôi cần truy vấn chọn cho bảng này để chỉ trả về hàng đầu tiên được tìm thấy nơi có các bản sao trong 'CName'. Đối với bảng này, nó sẽ trả về tất cả các hàng ngoại trừ hàng thứ 3 (hoặc hàng thứ nhất - bất kỳ địa chỉ nào trong hai địa chỉ đó đều được nhưng chỉ có thể trả về một địa chỉ). Có từ khóa nào tôi có thể thêm vào truy vấn CHỌN để lọc dựa trên việc máy chủ đã xem giá trị cột trước đó chưa?

Câu trả lời:


125

Một câu trả lời rất đơn giản nếu bạn nói rằng bạn không quan tâm địa chỉ nào được sử dụng.

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

Nếu bạn muốn đầu tiên theo, giả sử, một cột "được chèn" thì đó là một truy vấn khác

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

Mặc dù nó có thể không được sử dụng theo cách này khi chọn 10 cột. Cũng có vẻ như nó không thể chấp nhận một cột kiểu bit.
nuit9

1
@ nuit9: tất nhiên nó sẽ không hoạt động với bit và 10 cột. Cả hai sự kiện này đều không nằm trong câu hỏi của bạn. Bạn sẽ sử dụng kỹ thuật thứ 2 hoặc kỹ thuật của Ben Thul. Tôi đã trả lời những gì bạn hỏi cụ thể, với các gợi ý về cách giải quyết tổng quát hơn.
gbn

Phần đầu tiên NÊN làm việc với nhiều cột, mặc dù không phải với các cột kiểu bit. Tuy nhiên, tôi đã thử nghiệm điều này trong MS SQL server 2016.
netfed

24

Trong SQL 2k5 +, bạn có thể làm điều gì đó như:

;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
Vui lòng giải thích những gì xếp hạng, phân vùng và [r] làm gì
Roberto

10

Bạn có thể sử dụng row_number()để lấy số thứ tự của hàng. Nó sử dụng overlệnh - partition bymệnh đề chỉ định thời điểm bắt đầu đánh số và order bylựa chọn thứ để đánh số hàng. Ngay cả khi bạn đã thêm một order byvào cuối truy vấn của mình, nó sẽ giữ nguyên thứ tự trong overlệnh khi đánh số.

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

6
Trong postgresql, các hàm cửa sổ không được phép trong mệnh đề WHERE
ekanna

3
Điều này không được phép đối với MS-SQL.
Mixxiphoid

1
ROW_NUMBER()không hoạt động trong Wheređiều khoản trong Teradata cũng
Pirate X

6

Bạn có thể sử dụng row_numer() over(partition by ...)cú pháp như sau:

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

Điều này làm là nó tạo ra một cột được gọi row, là một bộ đếm tăng lên mỗi khi nó thấy giống nhau CNamevà lập chỉ mục những lần xuất hiện đó AddressLine. Bằng cách áp đặt where row = 1, người ta có thể chọn cái CNameAddressLineđầu tiên theo thứ tự bảng chữ cái. Nếu order bydesc, thì nó sẽ chọn cái CNameAddressLinecuối cùng theo thứ tự bảng chữ cái.


1

Điều này sẽ cung cấp cho bạn một hàng của mỗi hàng trùng lặp. Nó cũng sẽ cung cấp cho bạn các cột kiểu bit, và nó hoạt động ít nhất trong 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

Nếu bạn muốn tìm tất cả các bản sao thay thế, chỉ cần thay đổi rn = 1 thành rn> 1. Hy vọng điều này sẽ giúp

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.