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

Sql 如何在查询中使用会计日历,sql,sql-server,Sql,Sql Server,我有两张桌子,一张是日历,一张是销售。这两个表没有链接在一起。我希望能够编写一个查询,根据会计日历计算一周内的总销售额。这可能吗?我们的会计日历从12月1日开始,每个月底都是星期五 任何帮助都将不胜感激 财政日历 Period Period1_StartDate Period1_EndDate Period2_StartDate Period2_EndDate........... 2018 01/12/2017 29/12/2017 30

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

任何帮助都将不胜感激

财政日历

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阶段开始日期和结束日期之间有4周的时间。第一周的前两次销售下降。所以第一周的总销售额将是43607

输出

WEEK      Total_Sales
W1        43607
W2        59213
W3        11111
W4        22222
CROSS的两个用途是将会计日历表正常化为一些有用的内容,然后是一个简单的分组

WHERE子句从会计日历中选择一行,从派生视图中选择期间或周或其他任何内容

如果那一周没有销售,就用左边的连接

ON子句查看会计日历和week派生视图,以防派生视图描述的周实际上不在该年中

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
CROSS的两个用途是将会计日历表正常化为一些有用的内容,然后是一个简单的分组

WHERE子句从会计日历中选择一行,从派生视图中选择期间或周或其他任何内容

如果那一周没有销售,就用左边的连接

ON子句查看会计日历和week派生视图,以防派生视图描述的周实际上不在该年中

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
UNPIVOT是一个非常有用的TSQL操作符,用于规范化数据。基本上,它获取一行并生成新行——在列列表中指定的每列对应一行

在下面的例子中,我在PeriodX_StartDate列上取消Pivot。我最初做了两个unpivot,另一个在PeriodX_EndDate。结束日期是不必要的,但如果您想知道我是如何加入两个unpivot的:我使用交叉应用程序为每周生成一个键,并使用谓词测试它们是否相等。如果没有谓词,则得到笛卡尔积

为了生成周数,我使用了一个交叉应用程序,与@MatBailie的答案中的交叉应用程序非常相似,只是我通过为每个周生成一组参数来避免联合,这些参数被传递到生成日期的函数中

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
UNPIVOT是一个非常有用的TSQL操作符,用于规范化数据。基本上,它获取一行并生成新行——在列列表中指定的每列对应一行

在下面的例子中,我在PeriodX_StartDate列上取消Pivot。我最初做了两个unpivot,另一个在PeriodX_EndDate。结束日期是不必要的,但如果您想知道我是如何加入两个unpivot的:我使用交叉应用程序为每周生成一个键,并使用谓词测试它们是否相等。如果没有谓词,则得到笛卡尔积

为了生成周数,我使用了一个交叉应用程序,与@MatBailie的答案中的交叉应用程序非常相似,只是我通过为每个周生成一组参数来避免联合,这些参数被传递到生成日期的函数中

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

根据您提供的示例,您可以执行以下操作:

SELECT *
FROM (
SELECT
    [YEAR]  = YEAR([Date]),
    [MONTH] = MONTH([Date]),
    [WEEK] = DATEPART(week, [Date]) - DATEPART(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)),
    SUM(Amount) OVER(PARTITION BY  YEAR([Date]), DATEPART(week, [Date]) - DATEPART(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0))+ 1) AS TotalSales
FROM Sales
)D 
GROUP BY 
    [YEAR], 
    [MONTH], 
    [WEEK], 
    TotalSales

这将为您提供与您在示例中提供的结果相同的结果,但是,由于我们不知道您的财政日历是如何配置的,您需要查看是否必须将其与之链接

根据您提供的示例,您可以执行以下操作:

SELECT *
FROM (
SELECT
    [YEAR]  = YEAR([Date]),
    [MONTH] = MONTH([Date]),
    [WEEK] = DATEPART(week, [Date]) - DATEPART(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0)),
    SUM(Amount) OVER(PARTITION BY  YEAR([Date]), DATEPART(week, [Date]) - DATEPART(week, DATEADD(MONTH, DATEDIFF(MONTH, 0, [Date]), 0))+ 1) AS TotalSales
FROM Sales
)D 
GROUP BY 
    [YEAR], 
    [MONTH], 
    [WEEK], 
    TotalSales

这将为您提供与您在示例中提供的结果相同的结果,但是,由于我们不知道您的财政日历是如何配置的,您需要查看是否必须将其与之链接

有多少周期起始日期列?13? 截止到第13天开始日期,第13天结束日期?如果可能的话,我会将财政日历反规范化为:年,周期号,开始日期,结束日期。这将使查询更容易@马特贝利是的,13@mikeS-事实上,我的大部分答案只是将最大中心归一化,这样剩下的部分就容易了。有多少周期起始日期列?13? 截止到第13天开始日期,第13天结束日期?如果可能的话,我会将财政日历反规范化为:年,周期号,开始日期,结束日期。这将使查询更容易@马特贝利是的,13@mikeS-事实上,我的大部分答案只是将最大中心归一化,这样剩下的部分就容易了。