'Find max value in a group based on various criteria

I have this table:

Level Code Hours
1 Code1 1000
2 Code1 134
3 Code1 1100
3 Code1 1300
4 Code1 234
4 Code1 1234
5 Code1 34
6 Code1 12
1 Code2 300
2 Code2 400
5 Code2 500
5 Code2 550
5 Code2 700
6 Code2 600
1 Code3 100
2 Code3 350
1 Code4 7200
5 Code4 150

If there are records for Level 3 and/or 4 then I need the max value between these level for the code. If level 3 and 4 are not present for the code then get max value of level 5 and/or 6. If neither of level 3, 4, 5 and 6 are present then the query should return max value of level 1 and/or 2.

So the result of above table should be like:

Level Code Hours
3 Code1 1300
5 Code2 700
2 Code3 350
5 Code4 150

I have tried this:

SELECT * INTO #tempMaxTimeEntryData FROM
(SELECT LEVEL, CODE, HOURS = MAX(HOURS) FROM tempTimeEntryData 
 GROUP BY LEVEL, CODE) maxtemp

SELECT CODE, HOURS FROM #tempMaxTimeEntryData GROUP BY CODE
HAVING MAX(HOURS) = 
(SELECT HOURS= MAX(HOURS),CODE FROM #tempMaxTimeEntryData 
 WHERE 1 = (CASE WHEN LEVEL IN (3.0,4.0) THEN 1
               WHEN LEVEL NOT IN (3.0,4.0) AND LEVEL IN (5.0,6.0) THEN 1
               WHEN LEVEL NOT IN (3.0,4.0,5.0,6.0) AND LEVEL IN (1.0,2.0) THEN 1
               END) GROUP BY CODE)

This is returning wrong data for Code4. Instead of Level 5, it is returning below:

Level Code Hours
1 Code4 7200

New data:

Employee Level Code Hours
Danilo 2 Code1 1080
Akash 1 Code2 82.7
Shradha 2 Code2 838.7
Shalini 3 Code2 813.6
Priyanka 3.5 Code2 2684.4
Gagan 4 Code2 3240
Manu 5 Code2 870
Akash 1 Code3 604.2
Shradha 2 Code3 3372
Manu 5 Code3 1230
Ashu 6 Code3 2270
Akash 1 Code4 448.8
Shradha 2 Code4 2375.4
Shalini 3 Code4 2811
Priyanka 3 Code4 2811
Ritu 4 Code4 85.2
Praveen 5 Code4 67.2
Rakesh 1 Code5 47580
Sampath 5 Code5 339

Output should be:

Employee Level Code Hours
Danilo 2 Code1 1080
Gagan 4 Code2 3240
Ashu 6 Code3 2270
Priyanka 3 Code4 2811
Sampath 5 Code5 339


Solution 1:[1]

You just want to apply an order to the rows (per code) and then pick out the ones at the top:

WITH Data as (
    SELECT Code, Level, Hours,
        ROW_NUMBER() OVER (
            PARTITION BY Code ORDER BY
                CASE WHEN Level IN (3, 4) THEN 1
                     WHEN Level IN (5, 6) THEN 2
                     WHEN Level IN (1, 2) THEN 3 END,
                Hours DESC
                --, Employee
        ) AS rn
    FROM tab
)
SELECT * FROM DATA WHERE rn = 1;

Solution 2:[2]

You can use the ROW_NUMBER window function, that will partition the Code column and order on the Level field (according to your priority requirement of [3,4] > [5,6] > [1,2]) and on the Hours descending.

Once you have the max amount of hours filtered out with your requirement, you can select for each row, which Hours field value corresponds to the extracted maximum value.

SELECT Employee,
       Level,
       Code,
       Hours
FROM (SELECT tab.*,
             ROW_NUMBER() OVER(
                 PARTITION BY Code
                 ORDER BY CASE WHEN Level < 3 THEN 2
                               WHEN Level > 4 THEN 1
                               ELSE                0 END, 
                          Hours DESC        ) AS HoursOrder
      FROM tab)   ordered_hours
WHERE HoursOrder = 1

Try it here.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 shawnt00
Solution 2