2009-07-31 47 views
12

Vì vậy, tôi biết đây là một câu hỏi khá ngớ ngẩn, tuy nhiên (như tiêu đề khá lengthily nói) Tôi muốn biết làm thế nào làm như sau:Làm thế nào để bạn nhóm theo một cột và truy xuất một hàng có giá trị nhỏ nhất của một cột khác trong T/SQL?

Tôi có một bảng như thế này:

ID Foo Bar Blagh 
---------------- 
1 10 20 30 
2 10 5 1 
3 20 50 40 
4 20 75 12 

tôi muốn nhóm theo Foo, sau đó kéo ra các hàng có Bar tối thiểu, tức là tôi muốn như sau:

ID Foo Bar Blagh 
---------------- 
2 10 5 1 
3 20 50 40 

Tôi không thể cho cuộc sống của mình tìm ra đúng SQL để lấy lại. Tôi muốn một cái gì đó như:

SELECT ID, Foo, Bar, Blagh 
FROM Table 
GROUP BY Foo 
HAVING(MIN(Bar)) 

Tuy nhiên điều này rõ ràng không hoạt động vì đó là hoàn toàn hợp lệ CÓ cú pháp và ID, Foo, Bar và Blagh không được tổng hợp.

Tôi đang làm gì sai?

Trả lời

11

This gần như chính xác cùng một câu hỏi nhưng có một số câu trả lời!

Dưới đây là tôi chế giễu lên bảng:

declare @Borg table (
    ID int, 
    Foo int, 
    Bar int, 
    Blagh int 
) 
insert into @Borg values (1,10,20,30) 
insert into @Borg values (2,10,5,1) 
insert into @Borg values (3,20,50,70) 
insert into @Borg values (4,20,75,12) 

Sau đó, bạn có thể làm một bên trong vô danh tham gia để có được những dữ liệu mà bạn muốn.

select B.* from @Borg B inner join 
(
    select Foo, 
     MIN(Bar) MinBar 
    from @Borg 
    group by Foo 
) FO 
on FO.Foo = B.Foo and FO.MinBar = B.Bar 

EDIT Adam Robinson đã helpfully chỉ ra rằng "giải pháp này có khả năng quay trở lại nhiều hàng khi giá trị tối thiểu của Bar được nhân đôi, và loại bỏ bất kỳ giá trị của foo nơi thanh là null"

Tùy thuộc vào cách sử dụng của bạn, các giá trị trùng lặp trong đó Bar được nhân đôi có thể hợp lệ - nếu bạn muốn tìm tất cả các giá trị trong Borg nơi Bar tối thiểu, thì có cả hai kết quả dường như là cách để đi.

Nếu bạn cần phải nắm bắt NULLs trong lĩnh vực trên mà bạn đang tập hợp (bởi MIN trong trường hợp này), sau đó bạn có thể coalesce NULL với một giá trị chấp nhận được độ cao (hoặc thấp) (đây là một hack):

... 
MIN(coalesce(Bar,1000000)) MinBar -- A suitably high value if you want to exclude this row, make it suitably low to include 
... 

Hoặc bạn có thể sử dụng UNION và đính kèm tất cả các giá trị đó vào cuối kết quả của bạn.

on FO.Foo = B.Foo and FO.MinBar = B.Bar 
union select * from @Borg where Bar is NULL 

Sau này sẽ không giá trị nhóm trong @Borg với cùng giá trị Foo bởi vì nó không biết làm thế nào để lựa chọn giữa chúng.

+1

+1 để liên kết chứ không phải ném một số SQL với nhau nhanh nhất có thể ... – gbn

+2

Cheeky. Chỉnh sửa trong khi tôi nhận xét. – gbn

+1

@gbn Tôi đã "thêm giá trị" vào câu trả lời của mình! :) – butterchicken

1

Sự hiểu biết của tôi là bạn không thể thực sự làm điều này trong một lần.

select Foo, min(Bar) from table group by Foo 

,, cho bạn thanh tối thiểu cho mỗi Foo riêng biệt. Nhưng bạn không thể buộc mức tối thiểu đó với một ID cụ thể, bởi vì có thể có nhiều hơn một hàng với giá trị Bar đó.

Những gì bạn có thể làm là một cái gì đó như thế này:

select * from Table t 
join (
    select Foo, min(Bar) as minbar 
    from Table group by Foo 
) tt on t.Foo=tt.Foo and t.Bar=tt.minbar 

Lưu ý rằng nếu có nhiều hơn một hàng với giá trị tối thiểu Bar, bạn sẽ nhận được tất cả chúng với các truy vấn trên.

Bây giờ, tôi không tự xưng là một guru SQL, và nó là muộn tôi đang ở đâu, và tôi có thể bị thiếu một cái gì đó, nhưng có tôi $ 0.02 :)

1

này có thể giúp:

DECLARE @Table TABLE(
     ID INT, 
     Foo FLOAT, 
     Bar FLOAT, 
     Blah FLOAT 
) 

INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 1, 10 ,20 ,30 
INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 2, 10 ,5 ,1 
INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 3, 20 ,50 ,40 
INSERT INTO @Table (ID,Foo,Bar,Blah) SELECT 4, 20 ,75 ,12 

SELECT t.* 
FROM @Table t INNER JOIN 
     (
      SELECT Foo, 
        MIN(Bar) MINBar 
      FROM @Table 
      GROUP BY Foo 
     ) Mins ON t.Foo = Mins.Foo 
       AND t.Bar = Mins.MINBar 
3
select 
    ID, 
    Foo, 
    Bar, 
    Blagh 
from Table 
join (
    select 
    ID, 
    (row_number() over (order by foo, bar) - rank() over (order by foo)) as rowNumber 
) t on t.ID = Table.ID and t.rowNumber = 0 

Việc này lại được thêm vào bảng, nhưng lần này thêm số hàng tương đối cho giá trị cho bar, như thể nó được sắp xếp tăng dần trong mỗi giá trị foo. Bằng cách lọc trên rowNumber = 0, nó chỉ chọn giá trị thấp nhất cho bar cho mỗi giá trị foo. Điều này cũng có hiệu quả loại bỏ các khoản group by, vì bây giờ bạn chỉ lấy một hàng cho mỗi foo.

+0

Đáng nói đến điều này không hoạt động trước khi MSSQL2005 khi row_number() được giới thiệu. – butterchicken

1

Một lựa chọn khác sẽ là một cái gì đó dọc theo dòng trong các cách sau:

DECLARE @TEST TABLE( ID int, Foo int, Bar int, Blagh int) 
INSERT INTO @TEST VALUES (1,10,20,30) 
INSERT INTO @TEST VALUES (2,10,5,1) 
INSERT INTO @TEST VALUES (3,20,50,70) 
INSERT INTO @TEST VALUES (4,20,75,12) 

SELECT Id, Foo, Bar, Blagh 
FROM (
     SELECT id, Foo, Bar, Blagh, CASE WHEN (Min(Bar) OVER(PARTITION BY FOO) = Bar) THEN 1 ELSE 0 END as MyRow 
     FROM @TEST) t 
WHERE MyRow = 1 

Mặc dù điều này vẫn đòi hỏi một phụ truy vấn nó loại bỏ nhu cầu tham gia.

Chỉ một tùy chọn khác.

0
declare @Borg table (
    ID int, 
    Foo int, 
    Bar int, 
    Blagh int 
) 
insert into @Borg values (1,10,20,30) 
insert into @Borg values (2,10,5,1) 
insert into @Borg values (3,20,50,70) 
insert into @Borg values (4,20,75,12) 

select * from @Borg 

select Foo,Bar,Blagh from @Borg b 
where Bar = (select MIN(Bar) from @Borg c where c.Foo = b.Foo) 
+0

Đừng sao chép câu trả lời từ người khác. –

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