Given that you make reference to the row_number
window function in your question, I am going to assume that you are on at least SQL Server 2012.
Sample Data:
create table #myTable
(
MyDate datetime
, SalesTotal decimal(10,2)
)
insert into #myTable
values ('2017-12-14', 90.00)
, ('2017-12-15', 92.00)
, ('2017-12-18', 96.00)
, ('2017-12-19', 97.00)
, ('2017-12-20', 94.00)
, ('2017-12-21', 99.00)
, ('2017-12-22', 100.00)
Answer:
You can take advantage of the lag
/lead
window functions to compare values between rows. Note that the sub-query has the MyDate
value of 2017-12-14
in the record set because it needs to be present to get the SalesTotal
value for comparison, then the top level query filters that record out.
declare @bgn_dt date = '2017-12-15' --set by OP
, @end_dt date = '2017-12-22' --set by OP
, @lag_dt date;
set @lag_dt = (select max(MyDate) from #myTable where MyDate < @bgn_dt) --get the "yesterday" that the @bgn_dt will need
select a.MyDate
, a.SalesTotal
, format(((1.0 * a.SalesTotal) / a.SalesTotalPrevDay) - 1, '0%') as SalesTotalChange
from (
select t.MyDate
, t.SalesTotal
, lag(t.SalesTotal, 1, NULL) over (/*partition by (if needed)*/ order by t.MyDate asc) as SalesTotalPrevDay
from #myTable as t
where 1=1
and t.MyDate between @lag_dt and @end_dt
) as a
where 1=1
and a.MyDate >= @bgn_dt
Output:
+-------------------------+------------+------------------+
| MyDate | SalesTotal | SalesTotalChange |
+-------------------------+------------+------------------+
| 2017-12-15 00:00:00.000 | 92.00 | 2% |
| 2017-12-18 00:00:00.000 | 96.00 | 4% |
| 2017-12-19 00:00:00.000 | 97.00 | 1% |
| 2017-12-20 00:00:00.000 | 94.00 | -3% |
| 2017-12-21 00:00:00.000 | 99.00 | 5% |
| 2017-12-22 00:00:00.000 | 100.00 | 1% |
+-------------------------+------------+------------------+
Update:
In response to Pரதீப்'s comment, I thought I'd explain why someone might use where 1=1
in their query even though it seems unnecessary. Most of the time it is used to initialize the where
clause in SQL statement built at run time (Dynamic SQL). Another use for it is in development/debugging efforts where you may be commenting in/out various constraints including the first listed in the where
clause. In the end, it has no impact on the performance of the query, but may make your life easier.
Update 2:
To explain further as to why, per stackonfire's description, the first record is always null on the inner query. All window functions are only concerned with records that are in the current query.
For example, if you look at the query below, you would want row_number
to return 1, 2, 3 (as opposed to 1, 2, 4). The same is true of a lag
/lead
function, that some record has to be the first/last therefore there is no previous/next record to retrieve a value from.
I have updated my initial answer to add the dates necessary to the where
clause.
create table #letters
(
letter char(1)
)
insert into #letters
values ('a'), ('b'), ('c'), ('d')
select *
, row_number() over (order by l.letter asc) as row_nbr
from #letters l
where l.letter <> 'c'