如何按一个列分组并检索T / SQL中另一列的最小值的行?

时间:2009-07-31 11:44:28

标签: sql sql-server tsql

所以我知道这是一个非常愚蠢的问题,但是(正如相当冗长的标题所说)我想知道以下内容:

我有一张这样的表:

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

我想通过Foo分组,然后用最小的Bar拉出行,即我想要以下内容:

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

我不能为我的生活找出正确的SQL来检索它。我想要这样的东西:

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

然而,这显然不起作用,因为完全无效HAVING语法和ID,Foo,Bar和Blagh没有聚合。

我做错了什么?

6 个答案:

答案 0 :(得分:11)

This几乎是完全相同的问题,但它有一些答案!

这是我嘲笑你的桌子:

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

编辑亚当·罗宾逊已经帮助指出“当Bar的最小值重复时,此解决方案有可能返回多行,并消除任何值为foo的值{{1} }“

根据您的用例,Bar重复的重复值可能是有效的 - 如果您想在Borg中找到Bar最小的所有值,那么两种结果似乎都是可行的。

如果你需要在你聚合的字段中捕获null(在这种情况下是MIN),那么你可以NULLs具有可接受的高(或低)值的NULL(这个是一个黑客):

coalesce

或者您可以使用UNION并将所有此类值附加到结果集的底部。

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

后者不会将@Borg中的值与on FO.Foo = B.Foo and FO.MinBar = B.Bar union select * from @Borg where Bar is NULL 值相同,因为它不知道如何在它们之间进行选择。

答案 1 :(得分: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 

这会再次加入表格,但这次会为bar的值添加一个相对行号,就像它在foo的每个值中按升序排序一样。通过对rowNumber = 0进行过滤,它会为bar的每个值仅选择foo的最低值。这也有效地消除了group by子句,因为您现在每foo只检索一行。

答案 2 :(得分:1)

我的理解是你不可能一次性做到这一点。

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

,为每个不同的Foo提供最小的条形码。但是,您无法将该最小值与特定ID绑定,因为可能存在多个具有该Bar值的行。

你可以做的是这样的事情:

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

请注意,如果多个具有最小条形值的行,您将使用上述查询获取它们。

现在,我并不是自称是一个SQL大师,而且我已经很晚了,我可能会错过一些东西,但是我的价格是0.02美元:)

答案 3 :(得分:1)

这可能会有所帮助:

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

答案 4 :(得分:1)

另一种选择可能是以下几点:

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

虽然这仍然需要子查询,但它确实消除了对连接的需要。

只是另一种选择。

答案 5 :(得分: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)