2011-01-11 39 views
54

Hãy nói rằng tôi có một bảng các địa chỉ khách hàng:Làm thế nào để chọn chỉ có các hàng đầu tiên cho mỗi giá trị duy nhất của một cột

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ả lại hàng đầu tiên được tìm thấy trong đó 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ừ số thứ 3 (hoặc số 1 - bất kỳ địa chỉ nào trong số hai địa chỉ này đều ổn nhưng chỉ có thể trả về một). Có từ khóa nào tôi có thể thêm vào truy vấn SELECT để lọc dựa trên liệu máy chủ đã xem giá trị cột trước đó chưa?

Trả lời

82

Câu trả lời rất đơn giản nếu bạn nói 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 là người đầu tiên theo, chẳng hạn, một cột "chèn" thì đó là một truy vấn khác nhau

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 
+3

Sử dụng MIN với GROUP BY dường như hoạt động. – nuit9

+0

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

+0

@ 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 có 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

19

Trong SQL 2K5 +, bạn có thể làm một cái 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 
+0

Hãy giải thích những gì xếp hạng, phân vùng và [r] làm – Roberto

+5

Không được snarky, nhưng đọc tài liệu Wii hữu ích hơn bất cứ điều gì tôi có thể nói ở đây. Chỉ cần ném "xếp hạng hàm SQL" vào công cụ tìm kiếm ưa thích của bạn. Hãy cho tôi biết nếu bạn có bất kỳ câu hỏi cụ thể nào sau đó! –

9

Bạn có thể sử dụng row_number() để nhận số hàng của hàng. Nó sử dụng lệnh over - mệnh đề partition by chỉ định thời điểm khởi động lại đánh số và order by chọn thứ tự đặt hàng số. Ngay cả khi bạn đã thêm order by vào cuối truy vấn, nó sẽ giữ nguyên thứ tự trong lệnh over khi đánh số.

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

Trong postgresql, các chức năng cửa sổ không được cho phép trong mệnh đề WHERE – ekanna

+1

Điều này không được phép cho MS-SQL. – Mixxiphoid

+0

'ROW_NUMBER()' không hoạt động trong mệnh đề 'Where' trong Teradata –

0

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

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 không là nó tạo ra một cột gọi là row, đó là một bộ đếm mà increments mỗi lần nó thấy cùng CName, và lập chỉ mục những lần xuất hiện đó theo AddressLine. Bằng cách áp đặt where row = 1, người ta có thể chọn CName có số AddressLine xuất hiện theo thứ tự bảng chữ cái đầu tiên. Nếu số order bydesc, thì nó sẽ chọn số CName có số AddressLine đứng cuối bảng chữ cái.

Các vấn đề liên quan