我有一个看起来像这样的条目:
用户ID --- --- StatusStart --- StatusEnd --- StatusKey
StateDuration
Joe1 ------ 8:59:46 -------- 9:08:06 -------可用----- 500
我需要做的是将其分成两个条目。一个显示从8:59:46到9:00:00的声明,以及从9:00:00到9:08:06结束的第二个
最终,我希望能够拥有一些我可以应用于这个信息的大表的内容,这样我就可以确定代理商每半小时花费多少和多少时间。我过度思考这个问题,我很积极,因为我每次都把自己变成一个网络 - 我必须忘记一些简单的事情。
另外,理想情况下,无论它是什么公式,如果时间跨越多个区间,它都会起作用。例如StatusStart为8:59:46但StatusEnd为10:08:06。
答案 0 :(得分:3)
老派的方法,但我会创建第二个表:
CREATE TABLE SplitTimes (
SplitStart time not null,
SplitEnd time not null,
primary key (SplitStart, SplitEnd)
)
填充它:
INSERT INTO SplitTimes (SplitStart, SplitEnd) VALUES
('0:00', '0:30'),
('0:30', '1:00'),
('1:00', '1:30'),
('1:30', '2:00'),
('2:00', '2:30'),
('2:30', '3:00'),
('3:00', '3:30'),
('3:30', '4:00'),
('4:00', '4:30'),
('4:30', '5:00'),
('5:00', '5:30'),
('5:30', '6:00'),
('6:00', '6:30'),
('6:30', '7:00'),
('7:00', '7:30'),
('7:30', '8:00'),
('8:00', '8:30'),
('8:30', '9:00'),
('9:00', '9:30'),
('9:30', '10:00'),
('10:00', '10:30'),
('10:30', '11:00'),
('11:00', '11:30'),
('11:30', '12:00'),
('12:00', '12:30'),
('12:30', '13:00'),
('13:00', '13:30'),
('13:30', '14:00'),
('14:00', '14:30'),
('14:30', '15:00'),
('15:00', '15:30'),
('15:30', '16:00'),
('16:00', '16:30'),
('16:30', '17:00'),
('17:00', '17:30'),
('17:30', '18:00'),
('18:00', '18:30'),
('18:30', '19:00'),
('19:00', '19:30'),
('19:30', '20:00'),
('20:00', '20:30'),
('20:30', '21:00'),
('21:00', '21:30'),
('21:30', '22:00'),
('22:00', '22:30'),
('22:30', '23:00'),
('23:00', '23:30'),
('23:30', '23:59:59.9999999');
现在我可以运行:
SELECT e.UserID
,case when e.StatusStart >= t.SplitStart then e.StatusStart else t.SplitStart end as SplitStatusStart
,case when e.StatusEnd <= t.SplitEnd then e.StatusEnd else t.SplitEnd end as SplitStatusEnd
,e.StatusKey
,datediff(second, case when e.StatusStart >= t.SplitStart then e.StatusStart else t.SplitStart end,
case when e.StatusEnd <= t.SplitEnd then e.StatusEnd else t.SplitEnd end) SplitStatusDuration
FROM EntryTable e
INNER JOIN SplitTimes t
ON e.StatusStart <= t.SplitEnd
AND e.StatusEnd >= t.SplitStart
它可以使用日期时间而不是时间,并且可以在午夜工作。它只是额外摆弄从日期开始的时间,并重新添加另一个。
这样做的好处是不是递归CTE,它可能在大型桌面上表现更好。
答案 1 :(得分:0)
你可以像这样使用recursive CTE:
create table #temp (UserID nvarchar(100), StatusStart time, StatusEnd time, StatusKey nvarchar(100), StateDuration int)
insert into #temp (UserID, StatusStart, StatusEnd, StatusKey, StateDuration) values
('Joe1', '8:59:46', '9:08:06', 'Available', 500),
('Joe2', '8:59:46', '10:08:06', 'Available', 500)
;WITH cte AS (
SELECT UserID, StatusStart, CONVERT(time, DATEADD(hour, DATEDIFF(hour, 0, StatusStart) + 1, 0)) AS t, StatusEnd, StatusKey, StateDuration FROM #temp
UNION ALL
SELECT UserID, t, CONVERT(time, DATEADD(hour, 1, t)) AS t, StatusEnd, StatusKey, StateDuration FROM cte
WHERE t < StatusEnd
)
SELECT UserID, StatusStart, CASE WHEN t > StatusEnd THEN StatusEnd ELSE t END AS t, StatusKey, StateDuration FROM cte ORDER BY UserID, t
drop table #temp
CTE选择开始时间和下一个小时,然后使用最后一个小时作为下一个开始时间的联盟,直到下一个小时大于StatusEnd。我必须在time
添加一些转化为DATEADD
,因为datetimes
会返回C
。
结果:
答案 2 :(得分:0)
另一个选项
示例强>
Declare @YourTable Table ([UserID] varchar(50),[StatusStart] time,[StatusEnd] time,[StatusKey] varchar(50),[StateDuration] int)
Insert Into @YourTable Values
('Joe1','8:59:46','9:08:06','Available',500)
,('ZZZZ','8:59:46','10:08:06','Available',500) -- Added multi hour
Select A.UserID
,StatusStart = convert(time,IIF(DatePart(HOUR,A.StatusStart)=H,StatusStart,DateAdd(HOUR,H,0)))
,StatusEnd = convert(time,IIF(DatePart(HOUR,A.StatusStart)=H ,DateAdd(HOUR,H+1,0),IIF(H<DatePart(HOUR,StatusEnd),DateAdd(HOUR,H+1,0),StatusEnd)))
,A.StatusKey
,A.StateDuration
From @YourTable A
Join (
Select Top 24 H=-1+Row_Number() Over (Order By (Select NULL))
From master..spt_values n1
) B on H between DatePart(HOUR,A.StatusStart) and DatePart(HOUR,A.StatusEnd)
<强>返回强>
答案 3 :(得分:0)
这是一个内联表值函数解决方案,它使用数字表来创建时间段。我还为您的测试用例创建了一个示例表,以演示每个请求的输出。
DROP FUNCTION IF EXISTS itvf_Segments;
GO
CREATE FUNCTION itvf_Segments(@start TIME,@end TIME)
RETURNS TABLE
AS
RETURN
(
WITH N1 (Number) AS (SELECT 1 UNION ALL SELECT 1)
,N2 (Number) AS (SELECT 1 FROM N1 CROSS JOIN N1 AS N2)
,N3 (Number) AS (SELECT 1 FROM N2 CROSS JOIN N2 AS N3)
,N4 (Number) AS (SELECT 1 FROM N3 CROSS JOIN N3 AS N4)
,Numbers (Number) AS (SELECT TOP 48 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 FROM N4)
,Times (StartTime,EndTime) AS (SELECT TIMEFROMPARTS(Number/2,Number%2*30,0,0,0),DATEADD(minute,30,TIMEFROMPARTS(Number/2,Number%2*30,0,0,0)) FROM Numbers)
SELECT
CASE WHEN @start > T.StartTime THEN @start ELSE T.StartTime END AS NewStatusStart
,CASE WHEN @end < T.EndTime THEN @end ELSE T.EndTime END AS NewStatusEnd
FROM Times AS T
WHERE @start <= T.EndTime
AND @end >= T.StartTime
)
GO
;
DROP TABLE IF EXISTS UserStatus;
CREATE TABLE UserStatus
(
UserID VARCHAR(10)
,StatusStart TIME
,StatusEnd TIME
,StatusKey VARCHAR(25)
,StateDuration SMALLINT
)
;
INSERT INTO UserStatus
(UserID,StatusStart,StatusEnd,StatusKey,StateDuration)
VALUES
('Joe1','08:59:46','09:08:06','Available',500)
,('Joe2','08:59:46','10:08:06','Available',500)
;
SELECT *
FROM UserStatus
CROSS APPLY itvf_Segments(StatusStart,StatusEnd)
ORDER BY UserID,NewStatusStart,NewStatusEnd
;