每小时半点的值总和(那个小时)

edo*_*edo 0 sql-server aggregate date-math

我需要用 TS 聚合表的值的“总和”,每个半小时(60 分钟)的值列,即 00:30、01:30、02:30 等

样本数据:

Val TS
1   2019-08-12 00:00:00.013
3   2019-08-12 00:10:00.013
2   2019-08-12 00:20:00.013
2   2019-08-12 00:30:00.013
0   2019-08-12 00:40:00.013
0   2019-08-12 00:50:00.013
1   2019-08-12 01:00:00.013
7   2019-08-12 01:10:00.013
0   2019-08-12 01:20:00.013
1   2019-08-12 01:30:00.013
2   2019-08-12 01:40:00.013
0   2019-08-12 01:50:00.013
2   2019-08-12 02:00:00.013
0   2019-08-12 02:10:00.013
0   2019-08-12 02:20:00.013
0   2019-08-12 02:30:00.013
1   2019-08-12 02:40:00.013
0   2019-08-12 02:50:00.013
4   2019-08-12 03:00:00.013
3   2019-08-12 03:20:00.013
1   2019-08-12 03:30:00.013

Run Code Online (Sandbox Code Playgroud)

我们应该得到这个:

Val TS
10  2019-08-12 00:30:00
5   2019-08-12 01:30:00
8   2019-08-12 02:30:00
Run Code Online (Sandbox Code Playgroud)

有人可以就此提出建议吗?

谢谢,

Pau*_*ite 5

鉴于数据:

CREATE TABLE #Demo 
(
    Val integer NOT NULL, 
    TS datetime2(3) NOT NULL
);

INSERT #Demo 
    (Val, TS)
VALUES
(1, '2019-08-12 00:00:00.013'),
(3, '2019-08-12 00:10:00.013'),
(2, '2019-08-12 00:20:00.013'),
(2, '2019-08-12 00:30:00.013'),
(0, '2019-08-12 00:40:00.013'),
(0, '2019-08-12 00:50:00.013'),
(1, '2019-08-12 01:00:00.013'),
(7, '2019-08-12 01:10:00.013'),
(0, '2019-08-12 01:20:00.013'),
(1, '2019-08-12 01:30:00.013'),
(2, '2019-08-12 01:40:00.013'),
(0, '2019-08-12 01:50:00.013'),
(2, '2019-08-12 02:00:00.013'),
(0, '2019-08-12 02:10:00.013'),
(0, '2019-08-12 02:20:00.013'),
(0, '2019-08-12 02:30:00.013'),
(1, '2019-08-12 02:40:00.013'),
(0, '2019-08-12 02:50:00.013'),
(4, '2019-08-12 03:00:00.013'),
(3, '2019-08-12 03:20:00.013'),
(1, '2019-08-12 03:30:00.013');
Run Code Online (Sandbox Code Playgroud)

一种方法是:

  • 计算从固定日期开始的分钟数(没有时间分量)
  • 每次增加 30 分钟,将半小时时段调整为小时
  • 四舍五入到小时(使用整数数学将分钟除以然后乘以 60)
  • 减去30分钟
  • 将分钟添加到相同的固定日期

这显示在下面的代码中:

SELECT
    TS = CONVERT(char(19), Timeslot.TS, 120),
    Val = SUM(D.Val)
FROM #Demo AS D
CROSS JOIN (VALUES(CONVERT(datetime2(3), '20190101', 112))) AS Base(FixedDate)
CROSS APPLY(VALUES((DATEDIFF(MINUTE, Base.FixedDate, D.TS) + 30) / 60 * 60)) AS Slot(InMins)
CROSS APPLY(VALUES(DATEADD(MINUTE, Slot.InMins - 30, Base.FixedDate))) AS Timeslot(TS)
GROUP BY
    Timeslot.TS
ORDER BY
    Timeslot.TS;
Run Code Online (Sandbox Code Playgroud)

输出:

CREATE TABLE #Demo 
(
    Val integer NOT NULL, 
    TS datetime2(3) NOT NULL
);

INSERT #Demo 
    (Val, TS)
VALUES
(1, '2019-08-12 00:00:00.013'),
(3, '2019-08-12 00:10:00.013'),
(2, '2019-08-12 00:20:00.013'),
(2, '2019-08-12 00:30:00.013'),
(0, '2019-08-12 00:40:00.013'),
(0, '2019-08-12 00:50:00.013'),
(1, '2019-08-12 01:00:00.013'),
(7, '2019-08-12 01:10:00.013'),
(0, '2019-08-12 01:20:00.013'),
(1, '2019-08-12 01:30:00.013'),
(2, '2019-08-12 01:40:00.013'),
(0, '2019-08-12 01:50:00.013'),
(2, '2019-08-12 02:00:00.013'),
(0, '2019-08-12 02:10:00.013'),
(0, '2019-08-12 02:20:00.013'),
(0, '2019-08-12 02:30:00.013'),
(1, '2019-08-12 02:40:00.013'),
(0, '2019-08-12 02:50:00.013'),
(4, '2019-08-12 03:00:00.013'),
(3, '2019-08-12 03:20:00.013'),
(1, '2019-08-12 03:30:00.013');
Run Code Online (Sandbox Code Playgroud)

db<>小提琴演示

与问题中显示的输出相比,这会产生几个额外的时间段,但在我看来它仍然是正确的。


如果您更喜欢(有点慢)FORMAT功能,则以下等效:

SELECT
    TS = Slot.TS,
    Val = SUM(D.Val)
FROM #Demo AS D
CROSS APPLY
(
    VALUES
    (
        -- Remove 30 minutes
        DATEADD(MINUTE, -30, 
            CONVERT(datetime2(3),
                FORMAT(
                    -- Add 30 minutes
                    DATEADD(MINUTE, 30, D.TS), 
                    -- Truncate to hour
                    N'yyyy-MM-dd HH:00:00', 
                    N'en-us'
                ), 120
            )
        )
    )
) AS Slot (TS)
GROUP BY
    Slot.TS
ORDER BY
    Slot.TS;
Run Code Online (Sandbox Code Playgroud)