每个月的价值总和,其中数据包含开始和结束日期

时间:2018-11-28 11:00:19

标签: sql sql-server datetime sql-server-2012

我有一个简单的表,其中包含具有在开始日期和结束日期之间处于活动状态的特定值的项目。看起来像这样:

+----+---------+------------+------------+-------+
| Id | Project | StartDate  |  EndDate   | Value |
+----+---------+------------+------------+-------+
|  1 | AAA     | 2018-01-01 | NULL       |   100 |
|  2 | AAA     | 2018-04-12 | NULL       |   50  |
|  3 | BBB     | 2018-01-01 | 2018-03-01 |   20  |
|  4 | BBB     | 2018-01-01 | NULL       |   200 |
+----+---------+------------+------------+-------+

我想创建一个视图,该视图每月/项目呈现一个记录,显示该月“值”列的总和。示例:

+----+-------+---------+-------+
| Id | Month | Project | Value |
+----+-------+---------+-------+
|  1 | JAN   | AAA     |   100 |
|  2 | FEB   | AAA     |   100 |
|  3 | MAR   | AAA     |   100 |
|  4 | APR   | AAA     |   150 |
|  5 | MAY   | AAA     |   150 |
|  6 | JUN   | AAA     |   150 |
|  7 | JAN   | BBB     |   220 |
|  8 | FEB   | BBB     |   220 |
|  9 | MAR   | BBB     |   220 |
| 10 | APR   | BBB     |   200 |
| 11 | MAY   | BBB     |   200 |
| 12 | JUN   | BBB     |   200 |
+----+-------+---------+-------+

每月的哪一天都没有关系。例如,项目表中的ID 2显示,截至2018年4月12日,项目AAA获得的附加值为50。这意味着四月及以后的总和应为150。

对于项目BBB,您会看到一个项目于2018-03-01结束。这意味着BBB的总和应从APRIL开始减少20(而不是3月!),因为该项目在3月仍处于活动状态。

我想呈现直到当前月份(查询执行日期)的月份。 因此,在我的示例中,我已在2018年6月某个地方执行了此查询。

它必须在SQL Server 2012上运行。

以下是表格的脚本和一些虚拟数据:

CREATE TABLE [TestProject] (
    [Id] INT IDENTITY(1,1) PRIMARY KEY,
    [Project] NVARCHAR(50) NOT NULL,
    [StartDate] DATE NOT NULL,
    [EndDate] DATE NULL,
    [Value] INT NOT NULL
)

INSERT INTO [TestProject] ([Project], [StartDate], [EndDate], [Value])
VALUES
('AAA','2018-01-01',NULL,100),
('AAA','2018-04-12',NULL,50),
('BBB','2018-01-01','2018-03-01',200),
('BBB','2018-01-01',NULL,20);

1 个答案:

答案 0 :(得分:1)

您只需要构建一个介于MIN(StartDate)和今天之间的日期列表,剩下的事情很简单:

DECLARE @TestProject TABLE (Id INT IDENTITY(1, 1) PRIMARY KEY, Project NVARCHAR(50) NOT NULL, StartDate DATE NOT NULL, EndDate DATE NULL, Value INT NOT NULL);
INSERT INTO @TestProject VALUES
('AAA', '2018-01-01', NULL, 100),
('AAA', '2018-04-12', NULL, 50),
('BBB', '2018-01-01', '2018-03-01', 200),
('BBB', '2018-01-01', NULL, 20);

WITH cte AS (
    SELECT DATEADD(DAY, 1, EOMONTH((SELECT MIN(StartDate) FROM @TestProject), -1)) AS ym
    UNION ALL
    SELECT DATEADD(MONTH, 1, ym)
    FROM cte
    WHERE DATEADD(MONTH, 1, ym) <= CURRENT_TIMESTAMP
)

SELECT ym, Project, SUM(Value)
FROM cte
LEFT JOIN @TestProject ON DATEADD(DAY, 1, EOMONTH(StartDate, -1)) <= ym AND (
    EndDate IS NULL OR ym <= DATEADD(DAY, 1, EOMONTH(EndDate, -1))
)
GROUP BY ym, Project

在上面的示例中,DATEADD(DAY, 1, EOMONTH(expr, -1))函数用于生成指定日期的月初。