代码之家  ›  专栏  ›  技术社区  ›  ljs TheVillageIdiot

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

  •  13
  • ljs TheVillageIdiot  · 技术社区  · 15 年前

    所以我知道这是一个相当愚蠢的问题,但是(正如相当长的标题所说),我想知道如何做到以下几点:

    我有一张这样的桌子:

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

    我想按foo分组,然后用最小条拉出行,也就是说,我需要以下内容:

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

    但是,这显然不起作用,因为语法和id、foo、bar和blagh没有聚合是完全无效的。

    我做错什么了?

    6 回复  |  直到 10 年前
        1
  •  11
  •   Community gkalpak    7 年前

    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
    

    编辑 Adam Robinson很有帮助地指出,“当条的最小值被复制时,这个解决方案有可能返回多行,并消除条所在的foo值。” null

    根据您的使用情况,复制条的重复值可能是有效的-如果您想在borg中查找条最小的所有值,那么两个结果似乎都是可行的。

    如果你需要捕捉 NULLs 在要聚合的字段中(在本例中是按分钟),则可以 coalesce 具有可接受的高(或低)值的空值(这是一个hack):

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

    或者您可以进行联合,并将所有这些值附加到结果集的底部。

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

    后者不会将@borg中的值分组为相同的值 Foo 值,因为它不知道如何在它们之间进行选择。

        2
  •  3
  •   Adam Robinson    15 年前
    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 ,它只选择 酒吧 对于每个值 . 这也有效地消除了 group by 子句,因为现在每个 .

        3
  •  1
  •   Blorgbeard    15 年前

    我的理解是你不能一次就这么做。

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

    ,为每个不同的foo获取最小条。但是您不能将这个最小值绑定到一个特定的ID,因为可能有多行具有该条值。

    您可以这样做:

    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美元是这样的:)

        4
  •  1
  •   Adriaan Stander    15 年前

    这可能有助于:

    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
    
        5
  •  1
  •   HAdes    15 年前

    另一种选择是沿着以下几条线:

    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
    

    尽管这仍然需要一个子查询,但它确实消除了连接的需要。

    只是另一个选择。

        6
  •  0
  •   user3274352    10 年前
    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)