Home > OS >  Query to group by a type considering a grouping with a priority scale
Query to group by a type considering a grouping with a priority scale

Time:10-23

I need to group some data based on their types and considering a priority scale.

Consider this CTE below as an example.

WITH classif AS
(
    select 1 as id, 'account' as type, 'high' as priority from dual union all
    select 2 as id, 'account' as type, 'none' as priority from dual union all
    select 3 as id, 'account' as type, 'medium' as priority from dual union all
    select 4 as id, 'security' as type, 'high' as priority from dual union all
    select 5 as id, 'security' as type, 'medium' as priority from dual union all
    select 6 as id, 'security' as type, 'low' as priority from dual union all
    select 7 as id, 'security' as type, 'none' as priority from dual union all
    select 8 as id, 'transform' as type, 'none' as priority from dual union all
    select 9 as id, 'transform' as type, 'none' as priority from dual union all
    select 10 as id, 'transform' as type, 'none' as priority from dual union all
    select 11 as id, 'transform' as type, 'none' as priority from dual union all
    select 12 as id, 'enrollment' as type, 'medium' as priority from dual union all
    select 13 as id, 'enrollment' as type, 'low' as priority from dual union all
    select 14 as id, 'enrollment' as type, 'low' as priority from dual union all
    select 15 as id, 'enrollment' as type, 'low' as priority from dual;
    select 15 as id, 'process' as type, 'low' as priority from dual;
    select 15 as id, 'process' as type, 'none' as priority from dual;
    select 15 as id, 'process' as type, 'none' as priority from dual;
)

For this data set, my output has to be like this

------------ -------------
type        |  priority
------------ -------------
account     |  high
security    |  high
transform   |  none
enrollment  |  medium
process     |  low
---------------------------

The scale for priority is from "high" to "none"

The rules for the output must be like this

  • When a type has a line with priority "high", the output for that type must be "high".
  • When a type has a line with priority "medium" and do not have other with "high", the output must by "medium".
  • When a type has a line with priority "low" and do not have other with "high" or "medium", the output must by "low".
  • When a type has a line with priority "none" and do not have any other, the output must be "none"

I'm trying to do something like this query below, but this will return all lines and not grouping according to priorities

select 
    type,
    case 
        when priority = 'high' then 'high'
        when priority = 'medium' and priority <> 'high' then 'medium'
        when priority = 'medium' and priority <> 'high' then 'medium'
        when priority = 'low' and priority <> 'high' or priority <> 'medium' then 'low'
        when priority = 'none' and priority <> 'high' or priority <> 'medium' or priority <> 'low' then 'none'
        end as priority        
from classif
group by type,
    case
    when priority = 'high' then 'high'
        when priority = 'medium' and priority <> 'high' then 'medium'
        when priority = 'medium' and priority <> 'high' then 'medium'
        when priority = 'low' and priority <> 'high' or priority <> 'medium' then 'low'
        when priority = 'none' and priority <> 'high' or priority <> 'medium' or priority <> 'low' then 'none'
        end;

Can you help me solve this in the query?

CodePudding user response:

You can use ROW_NUMBER and order by a CASE statement to convert the priorities to a numerical value:

SELECT id, type, priority
FROM   (
  SELECT c.*,
         ROW_NUMBER() OVER (
           PARTITION BY type
           ORDER BY CASE priority
                    WHEN 'high'   THEN 1
                    WHEN 'medium' THEN 2
                    WHEN 'low'    THEN 3
                                  ELSE 4
                    END
         ) AS rn
  FROM   classif c
)
WHERE rn = 1;

Or, you can use MAX(...) KEEP (DENSE RANK FIRST ...) and order similarly using a CASE statement:

SELECT MAX(id) KEEP (
         DENSE_RANK FIRST
         ORDER BY CASE priority
                  WHEN 'high'   THEN 1
                  WHEN 'medium' THEN 2
                  WHEN 'low'    THEN 3
                                ELSE 4
                  END
       ) AS id,
       type,
       MAX(priority) KEEP (
         DENSE_RANK FIRST
         ORDER BY CASE priority
                  WHEN 'high'   THEN 1
                  WHEN 'medium' THEN 2
                  WHEN 'low'    THEN 3
                                ELSE 4
                  END
       ) AS priority
FROM   classif
GROUP BY type

Or, you can use a sub-query to store the relative priority levels and use a join:

SELECT MAX(c.id) KEEP (DENSE_RANK LAST ORDER BY p.id) AS id,
       type,
       MAX(c.priority) KEEP (DENSE_RANK LAST ORDER BY p.id) AS priority
FROM   classif c
       LEFT OUTER JOIN (
         SELECT 'high' AS priority, 3 As id FROM DUAL UNION ALL
         SELECT 'medium', 2 FROM DUAL UNION ALL
         SELECT 'low',    1 FROM DUAL UNION ALL
         SELECT 'none',   0 FROM DUAL
       ) p
       ON c.priority = p.priority
GROUP BY c.type

Which, for your sample data, all output:

ID TYPE PRIORITY
1 account high
12 enrollment medium
15 process low
4 security high
8 transform none

db<>fiddle here

  • Related