简体   繁体   中英

How to group the rows and get the count based on other column values in oracle

I have a table (with name 'jobs') having columns 'ID' and 'Type'. ID column can have same id more than once and the type column with have two values say 'A' and 'B'. I want to get the count of all the ids with type just 'A', with type just 'B' and with type 'A' and 'B' both.

I have tried the below query but it gives the count of type 'A' and type 'B'.

SELECT distinct 
       type,
       COUNT( 1 ) OVER ( PARTITION BY type) AS Count
FROM   jobs

Sample Data:

ID    Type    
1      A    
1      B    
2      A    
2      B    
3      A    
4      A    
5      A    
6      B    
7      B    
8      B    
9      B

Expected Output is:

Count of ID’s having just A as type: 3    
Count of ID’s having just B as type : 4    
Count of ID’s having A and B as type : 2    

You may try this.

--test data
with jobs(id,type) AS
(
select 1, 'A' FROM DUAL UNION ALL
select 1, 'A' FROM DUAL UNION ALL
select 2, 'A' FROM DUAL UNION ALL
select 2, 'A' FROM DUAL UNION ALL
select 2, 'B' FROM DUAL UNION ALL
select 3, 'A' FROM DUAL UNION ALL
select 3, 'B' FROM DUAL UNION ALL
select 4, 'B' FROM DUAL UNION ALL
select 4, 'B' FROM DUAL UNION ALL
select 5, 'B' FROM DUAL 
) --test data ends
select count(only_a) as "Count of Type A Only",
       count(only_b) as "Count of Type B Only",
       count(both_a_and_b) as "Count of Type A and B both"
FROM
(
SELECT
    ID,
    CASE WHEN MAX(TYPE)  = MIN(TYPE) and MIN(TYPE)  = 'A' THEN 1 END only_a,
    CASE WHEN MAX(TYPE)  = MIN(TYPE) and MIN(TYPE)  = 'B' THEN 1 END only_b,
    CASE WHEN COUNT(DISTINCT TYPE) = 2 THEN 1 END both_a_and_b
FROM jobs
WHERE type in ('A','B')
GROUP BY ID
)s;

Demo

I would write this as:

select sum(case when num_As > 0 and num_Bs = 0 then 1 else 0 end) as num_A_only,
       sum(case when num_As = 0 and num_Bs > 0 then 1 else 0 end) as num_B_only,
       sum(case when num_As > 0 and num_Bs > 0 then 1 else 0 end) as num_A_and_B       
from (select id,
             sum(case when type = 'A' then 1 else 0 end) as num_As,
             sum(case when type = 'B' then 1 else 0 end) as num_Bs
      from t
      group by id
     ) t;

If you want this in separate rows:

select (case when num_As > 0 and num_Bs = 0 then 'A_only'
             when num_As = 0 and num_Bs > 0 then 'B_only'
             when num_As > 0 and num_Bs > 0 then 'A_and_B'
        end) as grp,
       count(*)       
from (select id,
             sum(case when type = 'A' then 1 else 0 end) as num_As,
             sum(case when type = 'B' then 1 else 0 end) as num_Bs
      from t
      group by id
     ) t
group by (case when num_As > 0 and num_Bs = 0 then 'A_only'
               when num_As = 0 and num_Bs > 0 then 'B_only'
               when num_As > 0 and num_Bs > 0 then 'A_and_B'
          end);

Based on your sample data the below will work as expected:

with jobs(id,type) AS
    (
    select 1, 'A' FROM DUAL UNION ALL
    select 1, 'A' FROM DUAL UNION ALL
    select 2, 'A' FROM DUAL UNION ALL
    select 2, 'A' FROM DUAL UNION ALL
    select 2, 'B' FROM DUAL UNION ALL
    select 3, 'A' FROM DUAL UNION ALL
    select 3, 'B' FROM DUAL UNION ALL
    select 4, 'B' FROM DUAL UNION ALL
    select 4, 'B' FROM DUAL UNION ALL
    select 5, 'B' FROM DUAL 
    ) 
    SELECT distinct
            j1.type||j2.type as type,
           COUNT( DISTINCT j1.ID ) OVER ( PARTITION BY j1.type||j2.type) AS Count
    FROM   jobs j1 inner join jobs j2 on j1.id = j2.id and j1.type <= j2.type

Get 3 different counters for each case:

select 
  (select count(distinct id) from jobs j
   where ((select max(jobs.type) from jobs where jobs.id = j.id) = 'a')
  ) countera,
  (select count(distinct id) from jobs j
   where ((select min(jobs.type) from jobs  where jobs.id = j.id) = 'b')
  ) counterb,
  (select count(distinct id) from jobs j
   where (
      (select min(jobs.type) from jobs  where jobs.id = j.id) = 'a'
      and
      (select max(jobs.type) from jobs  where jobs.id = j.id) = 'b'
    )) counterab
from dual;

See the demo

To get 1 row for each counter:

select 
  'Count of IDs having just A as type :' descr,
  count(distinct id) counter from jobs j
where ((select max(jobs.type) from jobs where jobs.id = j.id) = 'a')
union all
select
  'Count of IDs having just B as type :' descr,
  count(distinct id) from jobs j
where ((select min(jobs.type) counter from jobs  where jobs.id = j.id) = 'b')
union all
select 
  'Count of IDs having A and B as type :' descr,
  count(distinct id) counter from jobs j
where 
  (
    (select min(jobs.type) from jobs  where jobs.id = j.id) = 'a'
    and
    (select max(jobs.type) from jobs  where jobs.id = j.id) = 'b'
  );

See the demo

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