Search code examples
sqloracleplsqloracle11goracle-sqldeveloper

How to partition my data by a specific date and another identifier SQL


with cte as
(
    select to_date('01-JUN-2020','DD-MON-YYYY')+(level-1) DT
    from dual
    connect bY level<= 30
)
select * 
from cte x
left outer join 
    (select date from time where emp in (1, 2)) a on x.dt = a.date

In this scenario I am trying to find the missing days that these persons didn't report to work... it works well for 1 person. I get back their missing days correctly. But when I add 2 persons.. I do not get back the correct missing days for them because I'm only joining on date I guess.

I would like to know how I can partition this data by the persons id and date to be able get accurate days that each were missing.

Please help, thanks.


Solution

  • You would typically cross join the list of dates with the list of persons, and then use not exists to pull out the missing person/date tuples:

    with cte as ( 
        select date '2020-06-01' + level - 1 dt
        from dual 
        connect by level <= 30 
    )
    select c.dt, e.emp
    from cte c
    cross join (select distinct emp from times) e
    where not exists (
        select 1
        from times t
        where t.emp = e.emp and t.dt = e.date
    )
    

    Note that this uses a literal date rather than to_date(), which is more appropriate here.

    This gives the missing tuples for all persons at once. If you want just for a predefined list of persons, then:

    with cte as ( 
        select date '2020-06-01' + level - 1 dt
        from dual 
        connect by level <= 30 
    )
    select c.dt, e.emp
    from cte c
    cross join (select 1 emp from dual union all select 2 from dual) e
    where not exists (
        select 1
        from times t
        where t.emp = e.emp and t.dt = e.date
    )
    

    If you want to also see the "presence" dates, then use a left join rather than not exists, as in your original query:

    with cte as ( 
        select date '2020-06-01' + level - 1 dt
        from dual 
        connect by level <= 30 
    )
    select c.dt, e.emp, -- enumerate the relevant columns from "t" here
    from cte c
    cross join (select 1 emp from dual union all select 2 from dual) e
    left join times t on t.emp = e.emp and t.dt = e.date