简体   繁体   中英

SQL: count rows where column = a value AND another column is the same as values in the group where the first condition is true?

I need to count the number of rows for each employee where the status = 9 and the date is the same as other rows within that employee_id where the status = 9 . I also want the employee_id and count_all , a count of all the rows for each employee.

Figuring out how to get the count_same_date_status_9 column is where I am stuck. These dates are made up just for the example so I dont want to put them explicitly into the query.

My table:

employee_id     status     date
1               9          10/19/2020
1               7          07/16/2001
1               9          10/19/2020
2               5          08/11/2011
2               9          12/25/2012
2               9          11/19/2013
3               5          06/05/2016
3               4          01/01/2021
4               9          02/15/2018
4               9          02/15/2018
4               9          02/15/2018

I want to return the following:

employee_id     count_same_date_status_9     count_all
1               2                            3
2               0                            3
3               0                            2
4               3                            3

You can use:

SELECT employee_id,
       MAX( CASE WHEN status = 9 AND cnt > 1 THEN cnt ELSE 0 END ) AS count_9,
       SUM( cnt ) AS count_all
FROM   (
  SELECT employee_id,
         status,
         dt,
         COUNT(*) AS cnt
  FROM   table_name
  GROUP BY employee_id, status, dt
)
GROUP BY employee_id
ORDER BY employee_id;

Which, for your sample data:

CREATE TABLE table_name ( employee_id, status, dt ) AS
SELECT 1, 9, DATE '2020-10-19' FROM DUAL UNION ALL
SELECT 1, 7, DATE '2001-07-16' FROM DUAL UNION ALL
SELECT 1, 9, DATE '2020-10-19' FROM DUAL UNION ALL
SELECT 2, 5, DATE '2011-08-11' FROM DUAL UNION ALL
SELECT 2, 9, DATE '2012-12-25' FROM DUAL UNION ALL
SELECT 2, 9, DATE '2013-11-19' FROM DUAL UNION ALL
SELECT 3, 5, DATE '2016-06-05' FROM DUAL UNION ALL
SELECT 3, 4, DATE '2021-01-01' FROM DUAL UNION ALL
SELECT 4, 9, DATE '2018-02-15' FROM DUAL UNION ALL
SELECT 4, 9, DATE '2018-02-15' FROM DUAL UNION ALL
SELECT 4, 9, DATE '2018-02-15' FROM DUAL;

Outputs:

\nEMPLOYEE_ID |  COUNT_9 |  COUNT_ALL \n----------: |  ------: |  --------: \n          1 |  2 |  3 \n          2 |  0 |  3 \n          3 |  0 |  2 \n          4 |  3 |  3 \n

db<>fiddle here

The logic you want is a bit counter-intuitive, but I think you can do this with two levels of aggregation:

select employee_id,
    case when max(cnt2) = 1 then 0 else max(cnt2) end count_same_date_status_9,
    sum(cnt1) count_all
from (
    select employee_id, dt, count(*) cnt1, sum(case when status = 9 then 1 else 0 end) cnt2
    from mytable t
    group by employee_id, dt
) t
group by employee_id
order by employee_id

Demo on DB Fiddle :

EMPLOYEE_ID | COUNT_SAME_DATE_STATUS_9 | COUNT_ALL
----------: | -----------------------: | --------:
          1 |                        2 |         3
          2 |                        0 |         3
          3 |                        0 |         2
          4 |                        3 |         3

The result can be achieved with a simple group by, then a subquery to find the most status = 9 that occured on the same day.

Query

WITH
    employees (employee_id, status, status_date)
    AS
        (SELECT 1, 9, TO_DATE ('10/19/2020', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 1, 7, TO_DATE ('07/16/2001', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 1, 9, TO_DATE ('10/19/2020', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 2, 5, TO_DATE ('08/11/2011', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 2, 9, TO_DATE ('12/25/2012', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 2, 9, TO_DATE ('11/19/2013', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 3, 5, TO_DATE ('06/05/2016', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 3, 4, TO_DATE ('01/01/2021', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 4, 9, TO_DATE ('02/15/2018', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 4, 9, TO_DATE ('02/15/2018', 'MM/DD/YYYY') FROM DUAL
         UNION ALL
         SELECT 4, 9, TO_DATE ('02/15/2018', 'MM/DD/YYYY') FROM DUAL)
  SELECT employee_id,
         NVL ((SELECT MAX (count_9)
                 FROM (  SELECT employee_id, status_date, COUNT (*) AS count_9
                           FROM employees e2
                          WHERE e2.status = 9 AND e2.employee_id = o.employee_id
                       GROUP BY employee_id, status_date)
               HAVING MAX (count_9) > 1),
              0)    AS count_same_date_status_9,
         count_all
    FROM (  SELECT employee_id, COUNT (*) AS count_all
              FROM employees e1
          GROUP BY employee_id) o
ORDER BY employee_id

Result

   EMPLOYEE_ID    COUNT_SAME_DATE_STATUS_9    COUNT_ALL
______________ ___________________________ ____________
             1                           2            3
             2                           0            3
             3                           0            2
             4                           3            3

this might work for MSSQL;

    select employee_id,sum(A),sum(B) from(
    select employee_id,count(*) A ,0 B from TABLENAME group by employee_id,status
    union all
    select employee_id,0 A,count(*) B from TABLENAME group by employee_id,date
    ) C group by employee_id

Someone gave this answer then deleted it so I'm not sure who to give credit to, but this query seems to be working as intended and is pretty simple to read:

select employee_id, sum(case when status = 9 then 1 else 0 end) as count_same_date_status_9, count(*) as count_all
from table_name
group by employee_id;

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