Nel L
Nel L

Reputation: 145

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!

Upvotes: 2

Views: 2450

Answers (2)

leftjoin
leftjoin

Reputation: 38335

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)

Upvotes: 1

Gordon Linoff
Gordon Linoff

Reputation: 1270573

You can do this with window (analytic) functions, which should be the fastest method. Your actually aggregation query is a bit unclear, but I think it is:

select date as current_date, sum(numbers) as current_sum
from t1 
group by date;

If you have values for all dates, then use:

select date as current_date, sum(numbers) as current_sum, 
       lag(sum(numbers), 7) over (order by date) as prev_7_sum
from t1 
group by date;

If you don't have data for all days, then use a window frame:

select date as current_date, sum(numbers) as current_sum, 
       max(sum(numbers), 7) over (order by date range between '7' day preceding and '7' day preceding) as prev_7_sum
from t1 
group by date;

Upvotes: 0

Related Questions