如何在查询中使用会计日历

时间:2018-05-23 17:59:33

标签: sql sql-server

我有两张桌子" Fiscal_calendar"和"销售"。这两个表没有链接在一起。我希望能够编写一个查询,根据财务日历计算一周内的总销售额。这可能吗?我们的财政日历从12月1日开始,每个月结束于星期五。

任何帮助都将不胜感激。

Fiscal_calendar

Period   Period1_StartDate   Period1_EndDate   Period2_StartDate   Period2_EndDate...........
2018        01/12/2017          29/12/2017     30/01/2018          26/01/2018 


Sales
Sales_order_no   Amount   Date        Customer
111              20453    03/12/2017  abc
112              23154    04/12/2017  bbb
113              20201    10/12/2017  ddd
114              39012    11/12/2017  ccc
115              11111    18/12/2017  eee
116              22222    25/12/2017  uuu

所以在第1期startdate和enddate之间有4周的时间。前两个销售额下降到第1周。因此第1周的总销售额将为43607

输出

WEEK      Total_Sales
W1        43607
W2        59213
W3        11111
W4        22222

2 个答案:

答案 0 :(得分:1)

CROSS APPLY的两种用途是将财务日历表中的(规范化)修复为有用的内容,然后是简单的GROUP BY

WHERE子句从会计日历中选择行,即派生视图中的句点或周(或其他)

如果当周没有销售,则LEFT JOIN

ON子句查看会计日历和周派生视图,以防派生视图描述的那一周不是真正的那一周。

SELECT
  p.year_id,
  p.period_id,
  w.week_id,
  SUM(s.amount)   AS total_amount
FROM
  fiscal_calendar    c
CROSS APPLY
(
            SELECT period,  1,  period1_startDate,  period1_endDate
  UNION ALL SELECT period,  2,  period2_startDate,  period2_endDate
  ..
  UNION ALL SELECT period, 13, period13_startDate, period13_endDate
)
  AS p(year_id, period_id, startDate, endDate)
CROSS APPLY
(
            SELECT 1, DATEADD(d,  0, startDate), DATEADD(d,  6, startDate)
  UNION ALL SELECT 2, DATEADD(d,  7, startDate), DATEADD(d, 13, startDate)
  UNION ALL SELECT 3, DATEADD(d, 14, startDate), DATEADD(d, 20, startDate)
  UNION ALL SELECT 4, DATEADD(d, 21, startDate), DATEADD(d, 27, startDate)
)
  AS w(week_id, startDate, endDate)
LEFT JOIN
  sales              s
    ON  s.date BETWEEN c.period1_startdate AND c.period13_enddate
    AND s.date BETWEEN w.startDate         AND w.endDate
WHERE
      p.year_id   = 2018
  AND p.period_id = 1
GROUP BY
  p.year_id,
  p.period_id,
  w.week_id

答案 1 :(得分:1)

UNPIVOT是一个有用的TSQL运算符,用于规范化数据。基本上它需要一行并创建新行 - 列中列中指定的每列一个。

在下面的情况下,我在PeriodX_StartDate列上进行了忽略。我最初做了两个非活动,另一个在PeriodX_EndDate上。结束日期是不必要的,但是如果你想知道我是如何加入这两个univots的:我使用交叉应用来生成每周的密钥,并且有一个谓词,测试它们是否相等。如果没有谓词,您将获得笛卡尔积。

为了生成周,我使用了与@MatBailie的答案非常相似的CROSS APPLY,除了我通过为每周生成一组参数来避免工会,这些参数被传递到生成日期的函数中。

WITH normalizedCal AS (
    SELECT [Period], MonthStart, WeekKey, StartDate, CutoffDate
    FROM fiscal_calendar cal
    UNPIVOT (
        MonthStart FOR StartKey IN (Period1_StartDate, Period2_StartDate, ..., PeriodN_StartDate)
    ) AS startInfo
    CROSS APPLY (
        SELECT 'WK' + CAST((StartIndex / 7) + 1 AS char(1)) [WeekKey]
            , DATEADD(day, startIndex, MonthStart) [StartDate]
            , DATEADD(day, EndIndex, MonthStart) [CutoffDate]
        FROM ( 
            VALUES ( 0, 7 ), ( 7, 14 ), ( 14, 21 ), ( 21, 28 )
        ) rangeValues ( StartIndex, EndIndex )
    ) weekInfo 
)
SELECT [Period], MonthStart, WeekKey, COALESCE(SUM(AMOUNT), 0) [Total_Sales]
FROM normalizedCal nc
LEFT JOIN sales ON sales.Date >= nc.StartDate AND sales.Date < nc.CutoffDate
GROUP BY [Period], MonthStart, WeekKey
ORDER BY [Period], MonthStart, WeekKey

包含样本数据的工作示例:

WITH normalizedCal AS (
    SELECT [Period], MonthStart, WeekKey, StartDate, CutoffDate
    FROM (
        VALUES (2018, '2017-12-01', '2017-12-29', '2017-12-30', '2018-01-26')
    ) cal ([Period],   Period1_StartDate,   Period1_EndDate, Period2_StartDate, Period2_EndDate)
    UNPIVOT (
        MonthStart FOR StartKey IN (Period1_StartDate, Period2_StartDate)
    ) AS startInfo
    CROSS APPLY (
        SELECT 'WK' + CAST((StartIndex / 7) + 1 AS char(1)) [WeekKey]
            , DATEADD(day, startIndex, MonthStart) [StartDate]
            , DATEADD(day, EndIndex, MonthStart) [CutoffDate]
        FROM ( 
            VALUES ( 0, 7 ), ( 7, 14 ), ( 14, 21 ), ( 21, 28 )
        ) rangeValues ( StartIndex, EndIndex )
    ) weekInfo 
),
sales AS (
    SELECT [Sales_order_no],[Amount], CAST([Date] as DATE) [Date],[Customer]
    FROM (VALUES 
        (111, 20453, '2017-12-03', N'abc'),
        (112, 23154, '2017-12-04', N'bbb'),
        (113, 20201, '2017-12-10', N'ddd'),
        (114, 39012, '2017-12-11', N'ccc'),
        (115, 11111, '2017-12-18', N'eee'),
        (116, 22222, '2017-12-25', N'uuu')
    ) [salessrc] ( [Sales_order_no],[Amount],[Date],[Customer])
)
SELECT [Period], MonthStart, WeekKey, COALESCE(SUM(AMOUNT), 0) [Total_Sales]
FROM normalizedCal nc
LEFT JOIN sales ON sales.Date >= nc.StartDate AND sales.Date < nc.CutoffDate
GROUP BY [Period], MonthStart, WeekKey
ORDER BY [Period], MonthStart, WeekKey