T-SQL加入自身,将今天的数据与昨天的数据进行比较

时间:2017-12-23 21:21:20

标签: sql-server tsql

例如,假设我有一个包含今天销售总额的表格,例如:

CREATE TABLE 
(
     MyDate DateTime, 
     SalesTotal Decimal(10,2)
)

数据输入M-F但不是周末输入。因此,对于某些缺失的日期,数据中总会存在间隙,这使得此查询更加困难。

我想写一个查询,显示每天的SalesTotal与[销售总额前一天]相比,以百分比表示。例如:

12-22  $100 in sales was 1% greater than day before
12-21  $99 in sales was 5% greater than day before
12-20  $94 in sales was -3% greater than day before
12-19  $97 in sales was 1% greater than day before
12-18  $96 in sales was 4% greater than day before
--- notice the weekend gap here ---
12-15  $92 in sales was 2% greater than day before

我相信我需要将表连接到自己或使用子查询但我仍然坚持如何将每行中附加的昨天数据添加到今天的数据中,以便我可以进行其他简单的数学计算,尤其是从昨天的日期开始并不总是“今天”的日期减去1.并且数据的自然顺序不是顺序的(并且它比我的例子更复杂)所以我不想使用ROW_NUMBER绑定到前一天的数据。我所知道的是,“昨天的”数据始终是[今天的数据之前的最后日期,即使不是前一天]。如何将今天的数据绑定到昨天的数据,以便我可以比较销售总额?

3 个答案:

答案 0 :(得分:2)

鉴于您在问题中引用了row_number窗口函数,我将假设您至少参加了SQL Server 2012.

示例数据:

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)

<强>答案:

您可以利用lag / lead窗口函数来比较行之间的值。请注意,子查询在记录集中的MyDate值为2017-12-14,因为它需要存在才能获得SalesTotal值进行比较,然后是顶级查询过滤器记录进行。

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

<强>输出:

+-------------------------+------------+------------------+
|         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%               |
+-------------------------+------------+------------------+

<强>更新

在回应Pரதீப்的评论时,我想我会在他们的查询中解释why someone might use where 1=1,即使这似乎是不必要的。大多数情况下,它用于初始化运行时构建的SQL语句中的where子句(动态SQL)。它的另一个用途是在开发/调试工作中,您可能正在评论各种约束,包括where子句中列出的第一个约束。最后,它有no impact on the performance of the query,但可能会让您的生活更轻松。

更新2:

进一步解释为什么,根据stackonfire的描述,第一条记录在内部查询上始终为null。所有窗口函数仅涉及当前查询中的记录。

例如,如果查看下面的查询,您希望row_number返回1,2,3(而不是1,2,4)。 lag / lead函数也是如此,某些记录必须是第一个/最后一个,因此没有上一个/下一个记录可以从中检索值。

我已更新了我的初始答案,以便为where子句添加必要的日期。

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'

答案 1 :(得分:2)

请尝试使用以下查询来获得所需的结果。

如果表格如下:

create table compare 
(  date1 date,
   amount decimal(10,2)
);

使用以下查询获取与上一日期相比的百分比变化

SELECT t1.rno, 
       t1.date1, 
       ( ( t1.amount - t2.amount ) / t1.amount ) * 100 AS percentage 
FROM   (SELECT Row_number() OVER(ORDER BY date1) RNO, 
               date1, 
               amount 
        FROM   compare) t1 
       INNER JOIN (SELECT Row_number() OVER(ORDER BY date1) RNO, 
                          date1, 
                          amount 
                   FROM   compare) t2 
               ON t2.rno + 1 = t1.rno 
ORDER  BY t1.rno 

请留言以便进行任何更改

答案 2 :(得分:1)

尝试使用此

    CREATE TABLE MyTable (MyDate DateTime, SalesTotal Decimal(10,2) )

    insert into myTable select '20171222 13:00',    100 union all
      select '20171222 13:01',  80  union all
      select '20171222 13:02',  50  union all
      select '20171221 10:00',  50  union all
      select '20171221 10:01',  75  union all
      select '20171221 10:02',  90  union all
      select '20171225 10:02',  45  union all
      select '20171225 10:03',  75  union all
      select '20171225 10:04',  60   



    select  sales.Date, sales.total,  
format(((1.0 * Sales.Total) / lead(sales.total, 1, Null) over (order by sales.Date asc) ) - 1, '0%') as SalesTotalChange

     from(
    select cast (a.MyDate as date) as 'Date', sum(salesTotal) total
    from MyTable a 
    group by cast (a.MyDate as date)) sales

编辑:上一个答案的复制格式