2009-07-31 17 views
12

Tak wiem, że to dość głupie pytanie, ale (jak mówi raczej dlugie tytuł) Chciałabym wiedzieć, jak wykonać następujące czynności:Jak grupować według jednej kolumny i pobrać wiersz z minimalną wartością innej kolumny w T/SQL?

Mam tabeli jak poniżej:

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

I chcą grupy przez Foo, a następnie wyciągnąć wiersze z minimalnym Bar, czyli chcę następujące:

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

nie mogę dla życia mnie poćwiczyć prawidłową SQL, aby pobrać ten. Chcę coś jak:

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

Jednak to wyraźnie nie działa tak, że jest zupełnie nieważne BIORĄC składni i identyfikator, Foo, bar i Blagh nie są sumowane.

Co robię źle?

Odpowiedz

11

This to prawie to samo pytanie, ale ma kilka odpowiedzi!

Oto ja wzornikiem tabelę:

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) 

Następnie można wykonać anonimowy sprzężenie wewnętrzne, aby uzyskać dane, które chcesz.

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 usłużnie wskazał, że „rozwiązanie to ma potencjał, aby wrócić wiele wierszy, gdy minimalna wartość Bar jest powielany i eliminuje jakąkolwiek wartość foo gdzie jest bar null

W zależności od twojego zastosowania, powielone wartości, w których Bar jest powielony, mogą być ważne - jeśli chcesz znaleźć wszystkie wartości w Borg, gdzie Bar był minimalny, wydaje się, że oba wyniki wydają się być najlepszym rozwiązaniem.

Jeśli trzeba uchwycić NULLs w polu, po której jesteś agregacji (przez min w tym przypadku), a następnie można coalesce NULL z zadowalająco wysoki (lub niski) wartości (jest to hack):

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

Albo możesz iść do UNION i dołączyć wszystkie takie wartości do dolnej części swojego zestawu wyników.

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

Ten ostatni nie będzie wartości grupowe w @Borg o tej samej wartości Foo ponieważ nie wiedzą jak wybierać między nimi.

+1

+1 za łączenie zamiast rzucać trochę kodu SQL tak szybko, jak można ... – gbn

+2

Bezczelny. Edytowanie podczas komentowania. – gbn

+1

@ gbn Byłem "dodając wartość" do mojej odpowiedzi! :) – butterchicken

1

Moje zrozumienie jest takie, że nie da się tego zrobić za jednym razem.

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

,, dostanie Ci minimalną poprzeczkę dla każdego odrębnego Foo. Ale nie można powiązać tego minimum z konkretnym ID, ponieważ może istnieć więcej niż jeden wiersz z tą wartością Bar.

Co można zrobić, to coś takiego:

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 

Należy pamiętać, że jeśli nie jest więcej niż jeden wiersz z wartością minimalną Bar, dostaniesz je wszystkie z powyższym zapytaniu.

Teraz nie jestem twierdząc być guru SQL i jest późno, gdzie jestem, a ja może brakować czegoś, ale nie mój $ 0,02 :)

1

To może pomóc:

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 

To dołączenie do tabeli ponownie, ale tym razem dodaje względny numer wiersza dla wartości dla bar, tak jakby był posortowany rosnąco w ramach każdej wartości foo. Filtrując pod numerem rowNumber = 0, wybiera tylko najniższe wartości dla bar dla każdej wartości foo. To również skutecznie eliminuje klauzulę group by, ponieważ obecnie pobierasz tylko jeden wiersz na foo.

+0

Warto wspomnieć, że nie działa przed MSSQL2005, kiedy wprowadzono row_number(). – butterchicken

1

Innym rozwiązaniem byłoby coś wzdłuż linii następujących czynności:

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 

Mimo to nadal wymaga sub-zapytanie to nie eliminuje potrzebę łączy.

Kolejna opcja.

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

Nie kopiuj odpowiedzi od innej osoby. –

Powiązane problemy