【问题标题】:Group by two columns, take sum, then max按两列分组,取总和,然后取最大值
【发布时间】:2019-10-01 04:41:04
【问题描述】:

共有三列:Id(字符)、Name(字符)和Score(整数)。

首先,我们按IdName 分组,并为每个组添加Score。让我们将添加的分数称为total_score

然后,我们按Name 分组,只取total_score 及其对应的IdName 中的最大值。我拥有其他一切,但我很难弄清楚如何获得Id。我得到的错误是

选择列表中的“Id”列无效,因为 它不包含在聚合函数或 GROUP BY 中 子句。

WITH Tmp AS
  (SELECT Id,
          Name,
          SUM(Score) AS total_score
   FROM Mytable
   GROUP BY Id,
            Name)
SELECT Name,  -- Id,
       MAX(total_score) AS max_score
FROM Tmp
GROUP BY Name
ORDER BY max_score DESC

【问题讨论】:

    标签: sql sql-server


    【解决方案1】:
    WITH Tmp AS
      (SELECT Id,
              Name,
              SUM(Score) AS total_score
       FROM Mytable
       GROUP BY Id,
                Name)
    SELECT Name,   Id,
           MAX(total_score) AS max_score
    FROM Tmp
    GROUP BY Name,id
    ORDER BY max_score DESC
    

    试试这个。希望这会有所帮助。

    【讨论】:

    • 因为,如果我们需要在select 中有id,你要么必须把它放在一个聚合函数中(max, sum, count 等),要么把它放在group by 中。错误说的是一样的。我们只能在 select 中包含那些包含在聚合函数中或存在于 group by 中的列
    • 如果多个id 具有相似的Name,那么您的答案将无效。 @d.b 如果每个 Name 都是唯一的,那么这个答案就可以了。
    • 此外,您的第二个select 将返回与Tmp 返回的行数完全相同的行,因为Tmp 具有每个idName 的唯一记录并再次使用GROUP BY不会影响记录。
    【解决方案2】:

    只需将row_number() 分区Name 添加到您的查询中并获取第一行(按total_score 降序排列)

    select  *
    from
    (
        -- your existing `total_score` query
        SELECT  Id, Name,
                SUM(Score) AS total_score,
                r = row_number() over (partition by Name order by SUM(Score) desc)
        FROM  Mytable
        GROUP BY Id, Name
    ) d
    where   r = 1
    

    【讨论】:

    • OP 没有提及或强调这种情况。如果确实发生了,可以使用dense_rank() 轻松处理
    【解决方案3】:
    WITH Tmp AS
     (
     SELECT Id,
              Name,
              SUM(Score) AS total_score
       FROM Mytable
       GROUP BY Id,
                NAME
     )
    SELECT Name,   Id,
           MAX(total_score) AS max_score
    FROM Tmp
    GROUP BY Name,id
    ORDER BY max_score DESC
    

    注意:- 如果我们使用聚合函数,那么我们必须使用其他列作为 Group By....

    在您的情况下,您使用 SUM(Score) 作为聚合函数,然后我们将其他列用作 Group by ...

    【讨论】:

    • 您的第二个select 将返回与Tmp 返回的行数完全相同的行数,因为Tmp 具有每个idName 的唯一记录并再次使用GROUP BY它不会对记录产生影响。
    【解决方案4】:

    我不确定以下查询的性能,但我们可以使用 window functions 从数据 partition 中获取 maximum 值。

    SELECT
            Id,
            Name,
            SUM(Score) AS total_score,
            MAX(SUM(Score)) OVER(Partition by Name) AS max_score
    FROM Mytable
    GROUP BY Id, Name;
    

    经过测试-

    declare @Mytable table (id int, name varchar(10), score int);
    insert into @Mytable values
    (1,'abc', 100),
    (2,'abc', 200),
    (3,'def', 300),
    (3,'def', 400),
    (4,'pqr', 500);
    

    输出 -

    Id  Name   total_score  max_score
    1   abc    100          200
    2   abc    200          200
    3   def    700          700
    4   pqr    500          500
    

    【讨论】:

      【解决方案5】:

      您可以选择带有total_score 列的DENSE_RANK(),然后选择带有Rank = 1 的记录。当有多个 Name 具有相同的 total_score 时,这也适用于那些。

      WITH Tmp AS
        (SELECT Id,
                Name,
                SUM(Score) AS total_score
         FROM Mytable
         GROUP BY Id, Name)
      SELECT Id, 
             Name,
             total_score AS max_score
      FROM (SELECT Id, 
                 Name,
                 total_score,
                 DENSE_RANK() OVER (PARTITION BY Name ORDER BY total_score DESC) AS Rank
          FROM Tmp) AS Tmp2
      WHERE Rank = 1
      

      【讨论】:

        【解决方案6】:

        你也可以试试这个:

        select id,name,max(total_score) over (partition by name)  max_score from (
        select id,name,sum(score) as total_score from YOURTABLE
        group by id,name
        ) t
        

        【讨论】:

          猜你喜欢
          • 2021-06-29
          • 2023-03-14
          • 1970-01-01
          • 2021-01-29
          • 2020-10-22
          • 2016-05-22
          • 1970-01-01
          • 1970-01-01
          • 2019-04-02
          相关资源
          最近更新 更多