Search code examples
sqloracle-databasecomparerows

Oracle sql compare rows within table / data set dynamically


following issue:

I want to compare the rows within a table with each other.

Let's assume I have a testcase table with below data constellation:

ID  Result  Date

123 success 29.04.2021
123 error   28.04.2021
123 success 27.04.2021
123 success 26.04.2021
123 error   25.04.2021

234 success 29.04.2021
234 success 28.04.2021
234 success 27.04.2021
234 success 26.04.2021
234 error   25.04.2021

I want my query to return only those ID's where the result was identical for the last 3 days. But I want to handle the days parameter dynamically without using PL/SQL. That mean's - if I need to compare the last 5 days I just want to change the paramter in my sql statement. Can that be realized?

Back to my example above:

I want my query to give back:

ID
234

Best regards


Solution

  • You can use just simple group by + having clause:

    select id
    from t
    where dt>=sysdate-3
    group by id
    having count(distinct result)=1 -- just one distinct `result`
    and count(*)=3 -- only 3 rows >= sysdate-3
    and count(distinct dt)=3 -- all 3 days are present;
    

    Full test cast with test data:

    with t(ID, Result, Dt) as (
    select 123, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'error  ' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual
    )
    select id
    from t
    where dt>=sysdate-3
    group by id
    having count(distinct result)=1 -- just one distinct `result`
    and count(*)=3 -- only 3 rows >= sysdate-3
    and count(distinct dt)=3 -- all 3 days are present
    ;
    

    Results:

            ID
    ----------
           234
    

    Modified query as per a clarification in the comment above:

    I want to check last 3 days starting from today. If I only have two entries then I would like to compare only two entries...

    select id
    from t
    where dt>=sysdate-3
    group by id
    having count(distinct result)=1 -- just one distinct `result`
    ;
    

    Full test case:

    with t(ID, Result, Dt) as (
    select 123, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'error  ' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual
    )
    select id
    from t
    where dt>=sysdate-3
    group by id
    having count(distinct result)=1 -- just one distinct `result`
    ;
    ----------------------
    --Results:
    
            ID
    ----------
           234
    

    PS. It's better to give test data in this format:

    with t(ID, Result, Dt) as (
    select 123, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'error  ' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
    select 123, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('29.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('28.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('27.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'success' , to_date('26.04.2021','dd.mm.yyyy') from dual union all
    select 234, 'error  ' , to_date('25.04.2021','dd.mm.yyyy') from dual
    )
    select *
    from t;