如何在 SQL 查询中选择每个组的第一行?

Posted

技术标签:

【中文标题】如何在 SQL 查询中选择每个组的第一行?【英文标题】:How do I select the first row per group in an SQL Query? 【发布时间】:2010-12-25 07:13:41 【问题描述】:

我有这个 SQL 查询:

SELECT   Foo, Bar, SUM(Values) AS Sum
FROM     SomeTable
GROUP BY Foo, Bar
ORDER BY Foo DESC, Sum DESC

这会产生类似这样的输出:

47  1   100
47  0   10
47  2   10
46  0   100
46  1   10
46  2   10
44  0   2

我希望每个 Foo 仅具有第一行(它及其最高 Bar),而忽略其余部分。

47  1   100
46  0   100
44  0   2

我该怎么做?

【问题讨论】:

那么,您使用的是什么数据库以及该数据库的哪个版本?这是一个很好的标签候选者! 选择保留哪些记录和丢弃哪些记录的标准是什么? @ILMV:有时我希望你能给好的编辑点数...... 重写了问题,简化了查询。除非更新以反映更改,否则某些答案可能不再有意义。 这能回答你的问题吗? Select first row in each GROUP BY group? 【参考方案1】:

仅对 Players.Nick 进行分组,然后选择描述的第一个(最少)

SELECT     Players.Nick, MIN(Reasons.Description), SUM(Marks.Value) AS Sum
FROM         Marks INNER JOIN
                      Players ON Marks.PlayerID = Players.ID INNER JOIN
                      Reasons ON Marks.ReasonId = Reasons.ID
GROUP BY Players.Nick
ORDER BY Players.Nick, Sum DESC

如果你总是想要第一个而不知道它

【讨论】:

我想要第三列中值最高的行。【参考方案2】:

(已编辑基于已编辑的问题) 然后,由于您希望根据聚合列的值进行过滤,因此您需要一个拥有子句。

  SELECT p.Nick, r.Description, SUM(m.Value) Sum
  FROM Marks m
    JOIN Players p
      ON m.PlayerID = p.ID 
    JOIN Reasons r 
      ON m.ReasonId = r.ID
  GROUP BY p.Nick, r.Description
  Having SUM(m.Value) =
      (Select Max(Sum) From
        (SELECT SUM(m.Value) Sum
         FROM Marks mi
           JOIN Players pi
              ON mi.PlayerID = pi.ID 
           JOIN Reasons r i
             ON mi.ReasonId = ri.ID
         Where pi.Nick = p.Nick
         GROUP BY pi.Nick, ri.Description))

  Order By p.Nick, Sum Desc

【讨论】:

我不想选择'X'的输出。相反,通过第三行中的值(见编辑)。【参考方案3】:

一般来说,尝试使用子查询而不是加入和分组 - 这通常会使 SQL 更容易理解。

SELECT Nick,
   (SELECT Description from Reasons WHERE Reasons.ID = (
       SELECT FIRST(Marks.ReasonId) from Marks WHERE Marks.PlayerID = Players.ID)
   ),
   (SELECT SUM(Value) from Marks WHERE Marks.PlayerID = Players.ID)

【讨论】:

【参考方案4】:

我可能不同意 rjmunru,因为使用 Ansii 样式的连接通常比子查询更容易阅读,但对于每个人来说,我只是按照我们的 DBA 所说的去做。

如果您只想要查询的第一个结果,您可以使用 rownum(如果使用 oracle,其他数据库可能有类似的东西)。

从 foo_t f 中选择 * 其中 f.bar = 'bleh' 和 rownum = 1

当然,HAVING 子句也可能是合适的,具体取决于您要执行的操作。

“HAVING 用于对由 GROUP BY 创建的组执行操作,类似于对基本 SQL 语句中行的 WHERE 子句的操作。WHERE 子句限制评估的行。HAVING 子句限制返回的分组行。”

【讨论】:

如果我理解正确,那么这就是我想要的,其实。【参考方案5】:

好奇。我能让这个工作的唯一方法是在内存中使用一个临时保存表。 (TSQL 语法)

-- original test data
declare @sometable table ( foo int, bar int, value int )

insert into @sometable values (1, 5, 10)
insert into @sometable values (1, 4, 20)
insert into @sometable values (2, 1, 1)
insert into @sometable values (2, 1, 10)
insert into @sometable values (2, 1, 1)
insert into @sometable values (2, 2, 13)
insert into @sometable values (3, 4, 25)
insert into @sometable values (3, 5, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)
insert into @sometable values (3, 1, 1)

-- temp table for initial aggregation
declare @t2 table (foo int, bar int, sums int)
insert into @t2
select foo, bar, sum(value) 
from @sometable
group by foo, bar

-- final result
select foo, bar, sums
from @t2 a
where sums = 
    (select max(sums) from @t2 b 
     where b.foo = a.foo)

【讨论】:

【参考方案6】:

SQL Server 2005 你可以使用这个:

声明@sometable table ( foo int, bar int, value int )

插入@sometable 值(1、5、10) 插入@sometable 值(1、4、20) 插入@sometable 值 (2, 1, 1) 插入@sometable 值 (2, 1, 10) 插入@sometable 值 (2, 1, 1) 插入@sometable 值 (2, 2, 13) 插入@sometable 值(3、4、25) 插入@sometable 值 (3, 5, 1) 插入@sometable 值 (3, 1, 1) 插入@sometable 值 (3, 1, 1) 插入@sometable 值 (3, 1, 1) 插入@sometable 值 (3, 1, 1) 插入@sometable 值(3、1、1)

-- 初始聚合的临时表 声明@t2 表(foo int,bar int,sums int) 插入@t2 选择 foo, bar, sum(value) 来自@sometable 按 foo、bar 分组

从 ( SELECT foo, bar, sums, ROW_NUMBER() OVER (PARTITION BY Foo ORDER BY Sums DESC) ROWNO FROM @t2) x 其中 x.ROWNO = 1

【讨论】:

【参考方案7】:
declare @sometable table ( foo int, bar int, value int )

insert into @sometable values (47, 1, 100)
insert into @sometable values (47, 0, 10)
insert into @sometable values (47, 2, 10)
insert into @sometable values (46, 0, 100)
insert into @sometable values (46, 1, 10)
insert into @sometable values (46, 2, 10)
insert into @sometable values (44, 0, 2)

WITH cte AS 
(
    SELECT   Foo, Bar, SUM(value) AS SumValue, ROW_NUMBER() OVER(PARTITION BY Foo ORDER BY FOO DESC, SUM(value) DESC) AS RowNumber
    FROM     @SomeTable
    GROUP BY Foo, Bar
)
SELECT * 
FROM cte
WHERE RowNumber = 1

【讨论】:

【参考方案8】:

这是一个旧帖子,但我今天遇到了同样的问题。我已经通过尝试许多查询来解决它,直到它起作用。我在 Visual Basic 2010 中使用 SQL Compact 3.5。

此示例适用于名为“TESTMAX”的表,其中包含“Id”(主键)、“nom”(名称)和“value”列,您可以使用它来获取每个“nom”的最大“值”行" :

SELECT TESTMAX.Id, TESTMAX.NOM, TESTMAX.Value
FROM     TESTMAX INNER JOIN
                  TESTMAX AS TESTMAX_1 ON TESTMAX.NOM = TESTMAX_1.NOM
WHERE  (TESTMAX.Value IN
                      (SELECT MAX(Value) AS Expr1
                       FROM      TESTMAX AS TESTMAX_2
                       WHERE   (NOM = TESTMAX_1.NOM)))
GROUP BY TESTMAX.Id, TESTMAX.NOM, TESTMAX.Value

如果要删除其他行,也可以使用:

DELETE FROM TESTMAX
WHERE  (Id NOT IN
                      (SELECT TESTMAX_3.Id
                       FROM      TESTMAX AS TESTMAX_3 INNER JOIN
                                         TESTMAX AS TESTMAX_1 ON TESTMAX_3.NOM = TESTMAX_1.NOM
                       WHERE   (TESTMAX_3.Value IN
                                             (SELECT MAX(Value) AS Expr1
                                              FROM      TESTMAX AS TESTMAX_2
                                              WHERE   (NOM = TESTMAX_1.NOM)))
                       GROUP BY TESTMAX_3.Id, TESTMAX_3.NOM, TESTMAX_3.Value))

【讨论】:

以上是关于如何在 SQL 查询中选择每个组的第一行?的主要内容,如果未能解决你的问题,请参考以下文章

如何编写查询以获取 SQL Server 中每个组的第一个条目? [复制]

如何根据多个排序列选择每组的第一行?

如何在 hive sql 中获取每个组的最大 row_number()

在 'groupby()' 和 'value_counts() 函数之后选择每个组的第一行

如何选择每组的第一行?

DolphinDB:如何获取每个滑动组的最大值的第一行?