简体   繁体   中英

Group by two columns, take sum, then max

There are three columns: Id (char), Name (char), and Score (int).

First, we group by Id and Name and add Score for each group. Let us call the added score total_score .

Then, we group by Name and take only the maximum of total_score and its corresponding Id and Name . I've got everything else but I'm having a hard time figuring out how to get the Id . The error I get is

Column 'Id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.

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

Try this. Hope this will help.

I am not sure about performance of below query but we can use window functions to get maximum value from data partition .

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

Tested -

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

Output -

Id  Name   total_score  max_score
1   abc    100          200
2   abc    200          200
3   def    700          700
4   pqr    500          500
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

Note:- If we are using aggregate function then we have to use other column as Group By....

In your case you are using SUM(Score) as aggregate function then we to use other column as Group by...

just add row_number() partition by Name to your query and get the 1st row (order by total_score descending)

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

You can select DENSE_RANK() with total_score column and then select records with Rank = 1 . This will work for those also when there are multiple Name which are having same 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

You can try this as well:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM