Search code examples
sqloraclewindow-functionslag

using LAG to compare the data from today and 7 days ago (not between)


I am currently trying to compare aggregated numbers from today and exactly 7 days ago (not between today and 7 days ago, but instead simply comparing these two discrete dates). I already have a way of doing it using a lot of subqueries, but the performance is bad, and I am now trying to optimize.

This is what I have come up with so far (sample query, not with real table names and columns due to confidentiality):

Select current_date, previous_date, current_sum, previous_sum, percentage
From   (Select date as current_date, sum(numbers) as current_sum, 
             lag (sum(numbers)) over (partition by date order by date) as previous_sum,
             (Select max(date)-7 From t1 ) as previous_date,
             (current_sum - previous_sum)*100/current_sum as percentage
        From t1 where date>=sysdate-7 group by date,previous_date)

But I am definitely doing something wrong since in the output the previous_sum appears null, and naturally the percentage too.

Any ideas on what I am doing wrong? I haven't used LAG before so it must be something there. Thanks!


Solution

  • Using Join of pre-aggregated subqueries.

    with agg as (
    select sum(numbers) as sum_numbers, date from t1 group by date
    )
    
    select curr.sum_numbers as current_sum, 
           prev.sum_numbers as prev_sum, 
           curr.date        as curr_date, 
           prev.date        as prev_date
      from agg curr
           left join agg prev on curr.date-7=prev.date 
    

    Using lag:

        with agg as (
        select sum(numbers) as sum_numbers, date from t1 group by date
        )
    
    select sum_numbers       as current_sum, 
           lag(sum_numbers, 7) over(order by date)  as prev_sum,
           a.date            as curr_date,
           lag(a.date,7) over(order by date) as prev_date
      from agg a
    

    If you want exactly 2 dates only (today and today-7) then it can be done much simpler using conditional aggregation and filter:

    select sum(case when date = trunc(sysdate) then numbers else null end) as current_sum, 
           sum(case when date = trunc(sysdate-7) then numbers else null end) as previous_sum, 
           trunc(sysdate)         as curr_date, 
           trunc(sysdate-7)       as prev_date,
           (current_sum - previous_sum)*100/current_sum as percentage
      from t1 where date = trunc(sysdate) or date = trunc(sysdate-7)