代码之家  ›  专栏  ›  技术社区  ›  J. Michiels

包含开始日期和结束日期的按月值总和

  •  3
  • J. Michiels  · 技术社区  · 6 年前

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

    +----+---------+------------+------------+-------+
    | 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-04-12,项目AAA的附加值为50。这意味着4月及以后的总数应为150。

    对于项目BBB,您将看到一个项目于2018-03-01结束。这意味着BBB的总额应随着4月(而非3月)的20而减少。因为这个项目在三月份仍在进行中。

    我要呈现截至当前月份(查询执行日期)的月份。 所以在我的示例中,我在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 回复  |  直到 6 年前
        1
  •  1
  •   Salman Arshad    6 年前

    您只需要构建一个介于min(startdate)和today之间的日期列表,其余的日期是直接的:

    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)) 函数用于生成指定日期的月初。