将同一张表自身联接在一起时,DATEADD如何工作?

时间:2019-04-04 21:34:27

标签: sql sql-server sql-server-2012 dateadd

我有一张每月生产值的表格。

示例:

Outdate | Prod Value | ID
2/28/19 |      110   | 4180
3/31/19 |      100   | 4180
4/30/19 |      90    | 4180

我还有一个表,其中包含每月的预测值。

示例:

Forecast End Date | Forecast Value  | ID
2/28/19           |   120           | 4180
3/31/19           |   105           | 4180
4/30/19           |    80           | 4180

我想创建一个表,该表的行包含ID,产品值,当前月份(例如3月)预测,上个月预测,下个月预测。

我想要什么:

ID | Prod Value | Outdate | Current Forecast | Previous Forecast | Next Forecast
4180 | 100      | 3/31/19 |              105 |               120 | 80

问题在于,当我使用DATEADD从上个月的“预测”表中引入特定值时,最终值中缺少了随机月份。

在下个月和上个月的预测中添加时,我尝试在DateDimension表中添加另一个LEFT JOIN / INNER JOIN,但这不能解决问题或添加太多行

我的DateDimension表具有以下列:DateKey 日期,日期,DaySuffix,工作日,WeekDayName,IsWeekend,IsHoliday,DOWInMonth,DayOfYear,WeekOfMonth,WeekOfYear,ISOWeekOfYear,Month,MonthName,Quarter,QuarterName,Year,MMYYYY,MonthYear,FirstDayOfMonth,LastDayOfMonth,FirstDayOfearter,FirstDayOfearter, FirstDayOfNextMonth,FirstDayOfNextYear

我的查询就是这样(为简单起见缩写)

SELECT A.ArchiveKey, BH.ID, d.[Date], BH.Outdate, BH.ProdValue, BH.Forecast, BHP.Forecast, BHN.Foreceast
FROM  dbo.BudgetHistory bh 
INNER JOIN dbo.DateDimension d ON bh.outdate = d.lastdayofmonth 
INNER JOIN dbo.Archive a ON bh.ArchiveKey = a.ArchiveKey 
LEFT JOIN  dbo.BudgetHistory bhp ON bh.ID = bhp.ID AND bhp.outdate = DATEADD(m, - 1, bh.Outdate) 
LEFT JOIN  dbo.BudgetHistory bhn ON bh.ID = bhn.ID AND bhn.outdate = DATEADD(m, 1, bh.Outdate)
WHERE        bh.ID IS NOT NULL

我得到这样的东西:

+------+------------+---------+------------------+-------------------+---------------+
|  ID  | Prod Value | Outdate | Current Forecast | Previous Forecast | Next Forecast |
+------+------------+---------+------------------+-------------------+---------------+
| 4180 |        110 | 2/28/19 |              120 | NULL              | NULL          |
| 4180 |        100 | 3/31/19 |              105 | 120               | 80            |
| 4180 |        90  | 4/30/19 |              80  | NULL              | NULL          |
+------+------------+---------+------------------+-------------------+---------------+

该模式似乎没有遵循任何合理的原则。 我希望为每一行填写值。

2 个答案:

答案 0 :(得分:2)

您可以联接表,然后使用窗口函数LEAD()LAG()恢复下一个和上一个预测值:

SELECT
    p.ID,
    p.ProdValue,
    p.Outdate,
    f.ForecastValue,
    LAG(f.ForecastValue) OVER(PARTITION BY f.ID ORDER BY f.ForecastEndDate) PreviousForecast,
    LEAD(f.ForecastValue) OVER(PARTITION BY f.ID ORDER BY f.ForecastEndDate) NextForecast
FROM prod p
INNER JOIN forecast f ON p.ID = f.ID AND p.Outdate = f.ForecastEndDate

demo on DB Fiddle 及其示例数据将返回:

  ID | ProdValue | Outdate             | ForecastValue | PreviousForecast | NextForecast
---: | --------: | :------------------ | ------------: | ---------------: | -----------:
4180 |       110 | 28/02/2019 00:00:00 |           120 |             null |          105
4180 |       100 | 31/03/2019 00:00:00 |           105 |              120 |           80
4180 |        90 | 30/04/2019 00:00:00 |            80 |              105 |         null

答案 1 :(得分:0)

DATEADD仅在新计算的值不是有效日期时才进行月末调整。因此DATEADD(month,-1,'20190331')产生2月28日。但是DATEADD(month,-1,'20190228')的产生时间是1月28日,而不是31日。

我可能会选择GMB's answer。如果您想基于DATEADD进行操作,则可以使用:

bhp.outdate = DATEADD(month, DATEDIFF(month,'20010131', bh.Outdate) ,'20001231')

这总是从bh.OutDate开始计算上个月的最后一天,但是它是通过将其计算为固定日期的偏移量,然后将该偏移量应用于不同的固定日期。

您可以颠倒2001013120001231的位置来计算下个月而不是前一个月。除了他们都拥有31天并希望应用“一个月”的关系外,对他们没有任何意义。