Sql server 按小时计算的总分钟数
我在数据表中有两个字段——“开始时间”和“结束时间”。这两个字段表示用户在特定任务上花费的持续时间。这些是varchar字段。因此,假设我们的开始时间为“21:05:00”,结束时间为“22:09:00”。我需要代码分别计算21小时和2200小时的总分钟数(即9分钟)。所以,不仅仅是一个简单的细微差别,而是一个小时的细分 最好的方法是什么 到目前为止,我已经创建了一个表,它将返回24小时内所有可能的小时数。以下是一个示例:Sql server 按小时计算的总分钟数,sql-server,sql-server-2005,Sql Server,Sql Server 2005,我在数据表中有两个字段——“开始时间”和“结束时间”。这两个字段表示用户在特定任务上花费的持续时间。这些是varchar字段。因此,假设我们的开始时间为“21:05:00”,结束时间为“22:09:00”。我需要代码分别计算21小时和2200小时的总分钟数(即9分钟)。所以,不仅仅是一个简单的细微差别,而是一个小时的细分 最好的方法是什么 到目前为止,我已经创建了一个表,它将返回24小时内所有可能的小时数。以下是一个示例: Hour startTime end
Hour startTime endTime
0 2015-01-01 00:00:00.000 2015-01-01 01:00:00.000
1 2015-01-01 01:00:00.000 2015-01-01 02:00:00.000
2 2015-01-01 02:00:00.000 2015-01-01 03:00:00.000
我将startTime字段从varchar转换为dateteime,并将其命名为sessionHour:
Convert(datetime, startTime) As sessionHour
此外,我还可以通过以下操作获得启动时间:
DateAdd(Minute, 60 * (DateDiff(Minute, 0, startTime) / 60), 0) As hourOf
除此之外,我还不知道如何解析每小时的分钟数。您可能需要查看
datepart()
函数,它将帮助您更清晰地操作日期。例如:
select @endOfHour = dateadd(hour,
1,
datetimefromparts(
datepart(year, @startTime),
datepart(month, @startTime),
datepart(day, @startTime),
datepart(hour, @startTime),
0,
0,
0));
select h.theHour, @startTime, @endOfHour, datediff(minute, @startTime, @endOfHour)
from ListOfHours as h
where datepart(hour, @startTime) = h.Hour;
你很接近。您只需要将数字表与数据结合起来。我会用它。下面是最终的解决方案 样本数据
DECLARE @Durations TABLE (ID int IDENTITY(1,1), StartTime datetime, EndTime datetime);
INSERT INTO @Durations VALUES
('2015-01-01 21:05:00', '2015-01-01 22:09:00'),
('2015-01-01 01:05:00', '2015-01-01 01:20:00'),
('2015-01-01 11:05:00', '2015-01-01 13:09:00'),
('2015-01-01 15:05:00', '2015-01-01 17:50:00'),
('2015-01-01 16:30:00', '2015-01-01 17:20:00');
我将从一开始就使用datetime
类型,因为您已将varchar
值转换为正确的datetime
我将使用一个数字表。它的行数应与数据中最长持续时间(小时)的行数相同。可能超过24个。一般来说,在数据库中为其他报告设置这样的表是很有用的
DECLARE @Numbers TABLE (Number int);
INSERT INTO @Numbers VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
-- Number of rows in this table should be more than the longest duration in hours
我需要一些时间参考点来删除datetime
的分钟部分。它可以是任何日期时间,只要没有分和秒
DECLARE @VarStart datetime;
SET @VarStart = '2000-01-01';
主要步骤-展开数据
最终查询
最后,我以小时为单位对分钟分组求和:
SELECT
HourStartFinal
,SUM(DATEDIFF(minute, MaxStart, MinEnd)) AS SumMinutesPerHour
FROM
@Durations AS D
CROSS APPLY
(
SELECT N.Number
FROM @Numbers AS N
WHERE N.Number <= DATEDIFF(hour, StartTime, EndTime)
) AS CA_Number
CROSS APPLY
(
SELECT
DATEADD(hour, CA_Number.Number, StartTime) AS HourStart
,DATEADD(hour, CA_Number.Number+1, StartTime) AS HourEnd
) AS CA_HourEnd
CROSS APPLY
(
-- Truncate to 1 hour.
SELECT
DATEADD(hour, DATEDIFF(hour, @VarStart, HourStart), @VarStart) AS HourStartFinal
,DATEADD(hour, DATEDIFF(hour, @VarStart, HourEnd), @VarStart) AS HourEndFinal
) AS CA_HourEndFinal
-- Intersect intervals [StartTime, EndTime] with [HourStartFinal, HourEndFinal]
CROSS APPLY
(
SELECT
CASE WHEN StartTime > HourStartFinal THEN StartTime ELSE HourStartFinal END AS MaxStart
,CASE WHEN EndTime < HourEndFinal THEN EndTime ELSE HourEndFinal END AS MinEnd
) AS CA_Intersect
GROUP BY HourStartFinal
ORDER BY HourStartFinal;
虽然弗拉基米尔·巴拉诺夫的答案是正确的,但它使用了许多交叉应用的方法 另一种按小时计算分钟数的方法是,在
starttime
和endtime
的范围内,除第一个小时和最后一个小时外,所有其他小时的minutediff
小时数为60
我们可以用它来构造我们的逻辑,类似这样的东西
DECLARE @UserTask TABLE (ID int IDENTITY(1,1),UserID INT,TaskID INT, StartTime datetime, EndTime datetime);
INSERT INTO @UserTask VALUES
(1,1,'2015-01-01 21:05:00', '2015-01-01 22:09:00'),
(1,1,'2015-01-01 01:05:00', '2015-01-01 01:20:00'),
(1,1,'2015-01-01 11:05:00', '2015-01-01 13:09:00'),
(1,1,'2015-01-01 15:05:00', '2015-01-01 17:50:00'),
(1,1,'2015-01-01 16:30:00', '2015-01-01 17:20:00'),
(2,2,'2015-01-01 21:05:00', '2015-01-01 22:09:00');
;WITH CTENum AS
(
SELECT 1 rn UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), CTEHours as
(
SELECT TOP 24 ROW_NUMBER()OVER(ORDER BY c1.rn) - 1 rn FROM CTENum c1 CROSS JOIN CTENum c2
)
SELECT ID,UserID,TaskID,StartTime,EndTime,rn as DayHour,
CASE WHEN r_asc = 1 AND r_desc = 1 THEN DATEDIFF(minute,StartTime,EndTime)
WHEN r_asc = 1 THEN 60 - DATEPART(minute,StartTime)
WHEN r_desc = 1 THEN DATEPART(minute,EndTime)
ELSE 60 END MinuteTime
FROM @UserTask
CROSS APPLY(
SELECT *,ROW_NUMBER()OVER(ORDER BY rn ASC) r_asc,ROW_NUMBER()OVER(ORDER BY rn DESC) r_desc
FROM CTEHours C
WHERE C.rn BETWEEN DATEDIFF(hour,CONVERT(VARCHAR(10),StartTime,112),StartTime) AND DATEDIFF(hour,CONVERT(VARCHAR(10),StartTime,112),EndTime)
) N
ORDER BY ID,DayHour
你说得对,我用了很多交叉应用来说明它对复杂公式的用处。我假设OP希望看到这个答案:55分钟为21小时;当开始时间=“21:05:00”和结束时间=“22:09:00”时,第22小时9分钟。总持续时间为55+9=64分钟。您的查询返回的是21小时5分钟,而不是55分钟。您的
案例
公式中一定遗漏了一些比较。此外,当持续时间跨越午夜边界时,解决方案不起作用。请尝试将源数据中的2015-01-01 22:09:00
更改为2015-01-03 22:09:00
。@Vladimir-关于最初的小时持续时间的好消息。我错过了60-x
。是的,我的解决方案在一天内有效:)。根据OP的问题和样本数据,他似乎没有跨越不同日期的记录。这非常有效,我对这把小提琴表示赞赏。非常感谢。我不清楚的一点是“数字”表以及它是如何连接所有这些的?numbers
表只包含从0到足够大的连续数字。在我的数据库中,它有10万行。您只需填写一次,并在需要时在多个报告中使用。在本例中,我显式地向其中添加了几行,以使示例正常工作。在实际系统中,该表已经有了这些数字,并且在Number
列上有主键/唯一索引。此表通过使用交叉应用
连接到主表,它为每个原始行创建了几行。例如,如果某些行的持续时间跨越3小时,则该行将在交叉应用后出现3次。
ID StartTime EndTime Number HourStart HourEnd HourStartFinal HourEndFinal MaxStart MinEnd MinutesPerHour
1 2015-01-01 21:05:00 2015-01-01 22:09:00 0 2015-01-01 21:05:00 2015-01-01 22:05:00 2015-01-01 21:00:00 2015-01-01 22:00:00 2015-01-01 21:05:00 2015-01-01 22:00:00 55
1 2015-01-01 21:05:00 2015-01-01 22:09:00 1 2015-01-01 22:05:00 2015-01-01 23:05:00 2015-01-01 22:00:00 2015-01-01 23:00:00 2015-01-01 22:00:00 2015-01-01 22:09:00 9
2 2015-01-01 01:05:00 2015-01-01 01:20:00 0 2015-01-01 01:05:00 2015-01-01 02:05:00 2015-01-01 01:00:00 2015-01-01 02:00:00 2015-01-01 01:05:00 2015-01-01 01:20:00 15
3 2015-01-01 11:05:00 2015-01-01 13:09:00 0 2015-01-01 11:05:00 2015-01-01 12:05:00 2015-01-01 11:00:00 2015-01-01 12:00:00 2015-01-01 11:05:00 2015-01-01 12:00:00 55
3 2015-01-01 11:05:00 2015-01-01 13:09:00 1 2015-01-01 12:05:00 2015-01-01 13:05:00 2015-01-01 12:00:00 2015-01-01 13:00:00 2015-01-01 12:00:00 2015-01-01 13:00:00 60
3 2015-01-01 11:05:00 2015-01-01 13:09:00 2 2015-01-01 13:05:00 2015-01-01 14:05:00 2015-01-01 13:00:00 2015-01-01 14:00:00 2015-01-01 13:00:00 2015-01-01 13:09:00 9
4 2015-01-01 15:05:00 2015-01-01 17:50:00 0 2015-01-01 15:05:00 2015-01-01 16:05:00 2015-01-01 15:00:00 2015-01-01 16:00:00 2015-01-01 15:05:00 2015-01-01 16:00:00 55
4 2015-01-01 15:05:00 2015-01-01 17:50:00 1 2015-01-01 16:05:00 2015-01-01 17:05:00 2015-01-01 16:00:00 2015-01-01 17:00:00 2015-01-01 16:00:00 2015-01-01 17:00:00 60
4 2015-01-01 15:05:00 2015-01-01 17:50:00 2 2015-01-01 17:05:00 2015-01-01 18:05:00 2015-01-01 17:00:00 2015-01-01 18:00:00 2015-01-01 17:00:00 2015-01-01 17:50:00 50
5 2015-01-01 16:30:00 2015-01-01 17:20:00 0 2015-01-01 16:30:00 2015-01-01 17:30:00 2015-01-01 16:00:00 2015-01-01 17:00:00 2015-01-01 16:30:00 2015-01-01 17:00:00 30
5 2015-01-01 16:30:00 2015-01-01 17:20:00 1 2015-01-01 17:30:00 2015-01-01 18:30:00 2015-01-01 17:00:00 2015-01-01 18:00:00 2015-01-01 17:00:00 2015-01-01 17:20:00 20
SELECT
HourStartFinal
,SUM(DATEDIFF(minute, MaxStart, MinEnd)) AS SumMinutesPerHour
FROM
@Durations AS D
CROSS APPLY
(
SELECT N.Number
FROM @Numbers AS N
WHERE N.Number <= DATEDIFF(hour, StartTime, EndTime)
) AS CA_Number
CROSS APPLY
(
SELECT
DATEADD(hour, CA_Number.Number, StartTime) AS HourStart
,DATEADD(hour, CA_Number.Number+1, StartTime) AS HourEnd
) AS CA_HourEnd
CROSS APPLY
(
-- Truncate to 1 hour.
SELECT
DATEADD(hour, DATEDIFF(hour, @VarStart, HourStart), @VarStart) AS HourStartFinal
,DATEADD(hour, DATEDIFF(hour, @VarStart, HourEnd), @VarStart) AS HourEndFinal
) AS CA_HourEndFinal
-- Intersect intervals [StartTime, EndTime] with [HourStartFinal, HourEndFinal]
CROSS APPLY
(
SELECT
CASE WHEN StartTime > HourStartFinal THEN StartTime ELSE HourStartFinal END AS MaxStart
,CASE WHEN EndTime < HourEndFinal THEN EndTime ELSE HourEndFinal END AS MinEnd
) AS CA_Intersect
GROUP BY HourStartFinal
ORDER BY HourStartFinal;
HourStartFinal SumMinutesPerHour
2015-01-01 01:00:00.000 15
2015-01-01 11:00:00.000 55
2015-01-01 12:00:00.000 60
2015-01-01 13:00:00.000 9
2015-01-01 15:00:00.000 55
2015-01-01 16:00:00.000 90
2015-01-01 17:00:00.000 70
2015-01-01 21:00:00.000 55
2015-01-01 22:00:00.000 9
DECLARE @UserTask TABLE (ID int IDENTITY(1,1),UserID INT,TaskID INT, StartTime datetime, EndTime datetime);
INSERT INTO @UserTask VALUES
(1,1,'2015-01-01 21:05:00', '2015-01-01 22:09:00'),
(1,1,'2015-01-01 01:05:00', '2015-01-01 01:20:00'),
(1,1,'2015-01-01 11:05:00', '2015-01-01 13:09:00'),
(1,1,'2015-01-01 15:05:00', '2015-01-01 17:50:00'),
(1,1,'2015-01-01 16:30:00', '2015-01-01 17:20:00'),
(2,2,'2015-01-01 21:05:00', '2015-01-01 22:09:00');
;WITH CTENum AS
(
SELECT 1 rn UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), CTEHours as
(
SELECT TOP 24 ROW_NUMBER()OVER(ORDER BY c1.rn) - 1 rn FROM CTENum c1 CROSS JOIN CTENum c2
)
SELECT ID,UserID,TaskID,StartTime,EndTime,rn as DayHour,
CASE WHEN r_asc = 1 AND r_desc = 1 THEN DATEDIFF(minute,StartTime,EndTime)
WHEN r_asc = 1 THEN 60 - DATEPART(minute,StartTime)
WHEN r_desc = 1 THEN DATEPART(minute,EndTime)
ELSE 60 END MinuteTime
FROM @UserTask
CROSS APPLY(
SELECT *,ROW_NUMBER()OVER(ORDER BY rn ASC) r_asc,ROW_NUMBER()OVER(ORDER BY rn DESC) r_desc
FROM CTEHours C
WHERE C.rn BETWEEN DATEDIFF(hour,CONVERT(VARCHAR(10),StartTime,112),StartTime) AND DATEDIFF(hour,CONVERT(VARCHAR(10),StartTime,112),EndTime)
) N
ORDER BY ID,DayHour