我在SQL Server Management Studio中获得了一个数据集。数据如下所示。我有记录的每个人userID
,date
,开始时间startime
和结束时间endtime
的标识符。
UserID date startime endtime
1 20110203 09:30 09:35
1 20110203 09:31 09:38
1 20110203 10:03 10:05
1 20110203 10:04:00 10:35:00
2 20110203 11:02 11:05
对于每个人,我想检查是否有重叠时间。如果有,我希望保留最小startime
和最大endtime
。如果没有重叠时间,我会保留原始数据。另外,我想计算maxi endtime
和smallest startime
的持续时间。
我想要的结果应该如下所示。任何人都可以教我如何编码。
UserID date startime endtime diff
1 20110203 09:30 09:38 00:08
1 20110203 10:03 10:35 00:02
2 20110203 11:02 11:05 00:03
答案 0 :(得分:1)
遵循我之前的cte方法的重新设计版本。但是,如果同一用户的多条记录具有相同的开始时间,它仍然会有问题...没有时间来修复那个,但据我所知,这在描述的过程中是不可能的!?< / p>
--
-- This part is temporary and has to be replaced by your tables
-- There several more records included now
-- There is still a glitch if the starttime is identical for two records - but as far as I understood, this is not possible in the described case?
--
declare @t table (userid int, date int, starttime time, endtime time);
insert into @t values (1, 20110203, '09:30:00', '09:35:00'), (1, 20110203, '09:31:00', '09:38:00'), (1, 20110203, '09:36:00', '09:41:00'), (1, 20110203, '10:03:00', '10:05:00'),(1, 20110203, '10:04:00', '10:35:00'),
(2, 20110203, '11:02:00', '11:05:00'), (2, 20110203, '11:03:00', '11:20:00'), (2, 20110203, '11:04:00', '11:35:00'), (2, 20110203, '13:02:00', '13:05:00'), (2, 20110203, '13:04:00', '13:15:00');
--
-- First cte: selects all start and endtimes and their - if existing - "overlaps"; recursive cte
--
WITH cte AS(
SELECT 1 lvl, a.userid
,CASE WHEN a.starttime <= ISNULL(b.starttime, a.starttime) THEN a.starttime ELSE b.starttime END AS starttime
,CASE WHEN a.endtime >= ISNULL(b.endtime, a.endtime) THEN a.endtime ELSE b.endtime END AS endtime
FROM @t as a
LEFT OUTER JOIN @t AS b ON b.userid = a.userid
AND b.starttime < a.starttime
AND b.endtime > a.starttime
UNION ALL
select a.lvl+1, a.userid
,CASE WHEN a.starttime <= ISNULL(b.starttime, a.starttime) THEN a.starttime ELSE b.starttime END AS xStart
,CASE WHEN a.endtime >= ISNULL(b.endtime, a.endtime) THEN a.endtime ELSE b.endtime END AS xEnd
from cte as a
INNER JOIN @t AS b ON b.userid = a.userid
AND b.starttime < a.starttime
AND b.endtime > a.starttime
),
--
-- Second cte: get the max. lvl result per user from the recursive cte
--
cteUserMaxLvl AS (
SELECT userid, max(lvl) AS MaxLvl
FROM cte
GROUP BY userid
),
--
-- third cte: get the rows matching the max.lvl; their timespan should cover the desired min. start and max. end
--
cteNoMoreOverlap AS(
SELECT a.userid, starttime, endtime
FROM cte AS a
JOIN cteUserMaxLvl AS b ON a.userid = b.userid AND a.lvl = b.MaxLvl
)
--
-- Select the rows from the "No more overlap" cte
--
SELECT userid, starttime, endtime
FROM cteNoMoreOverlap
UNION ALL
--
-- And finally select all rows, which are not covered by the previously selected timespan
--
SELECT a.userid, min(a.starttime) AS starttime, max(a.endtime) AS endtime
FROM cte AS a
JOIN cteNoMoreOverlap AS b ON a.userid = b.userid AND a.starttime NOT BETWEEN b.starttime AND b.endtime
GROUP BY a.userid
order by userid, starttime, endtime
答案 1 :(得分:1)
似乎带有CTE的SELECT需要以递归方式合并未确定数量的行。在这种情况下,我更喜欢安全的基于CURSOR的解决方案:
DECLARE @t TABLE
(
UserId int,
[Date] date,
StartTime time,
EndTime time
);
INSERT INTO @t VALUES
(1, '2011-02-03', '09:30:00', '09:35:00'),
(1, '2011-02-03', '09:31:00', '09:38:00'),
(1, '2011-02-03', '09:36:00', '09:41:00'),
(1, '2011-02-03', '09:40:00', '09:45:00'),
(1, '2011-02-03', '09:42:00', '09:43:00'),
(1, '2011-02-03', '10:03:00', '10:05:00'),
(2, '2011-02-03', '11:02:00', '11:05:00'),
(1, '2011-02-03', '12:00:00', '12:05:00'),
(1, '2011-02-03', '12:04:00', '12:06:00');
------------------
DECLARE @result TABLE
(
UserId int,
[Date] date,
StartTime time,
EndTime time
)
DECLARE cur CURSOR FOR
SELECT UserId, [Date], StartTime, EndTime
FROM @t
ORDER BY UserId, [Date], StartTime;
DECLARE @UserId int
DECLARE @Date date
DECLARE @StartTime time
DECLARE @EndTime time
DECLARE @LastUserId int
DECLARE @LastDate date
DECLARE @LastStartTime time
DECLARE @LastEndTime time
OPEN cur
FETCH NEXT FROM cur INTO @UserId, @Date, @StartTime, @EndTime
SET @LastUserId = @UserId
SET @LastDate = @Date
SET @LastStartTime = @StartTime
SET @LastEndTime = @EndTime
WHILE @@FETCH_STATUS = 0
BEGIN
IF @UserId = @LastUserId AND @Date = @LastDate AND @StartTime <= @LastEndTime
SET @LastEndTime = CASE WHEN @LastEndTime > @EndTime THEN @LastEndTime ELSE @EndTime END
ELSE
BEGIN
INSERT @result(UserId, [Date], StartTime, EndTime) VALUES (@LastUserId, @LastDate, @LastStartTime, @LastEndTime)
SET @LastUserId = @UserId
SET @LastDate = @Date
SET @LastStartTime = @StartTime
SET @LastEndTime = @EndTime
END
FETCH NEXT FROM cur INTO @UserId, @Date, @StartTime, @EndTime
END
INSERT @result(UserId, [Date], StartTime, EndTime) VALUES (@LastUserId, @LastDate, @LastStartTime, @LastEndTime)
CLOSE cur
DEALLOCATE cur
SELECT UserId,
[Date],
StartTime,
EndTime,
CAST(DATEADD(second,DATEDIFF(second,StartTime,EndTime),'2000-01-01') AS time) Diff
FROM @result
返回
1 2011-02-03 09:30:00.0000000 09:45:00.0000000 00:15:00.0000000
1 2011-02-03 10:03:00.0000000 10:05:00.0000000 00:02:00.0000000
1 2011-02-03 12:00:00.0000000 12:06:00.0000000 00:06:00.0000000
2 2011-02-03 11:02:00.0000000 11:05:00.0000000 00:03:00.0000000
答案 2 :(得分:0)
我相信当你说重叠时间时,你会在同一天的同一时间内说。如果这就是您的意思,以下解决方案可能会奏效。附件是我的搜索结果的屏幕截图。
CREATE TABLE #OverlappingDates
(
UserID INT
, [date] DATE
, starttime VARCHAR(5)
, endtime VARCHAR(5)
);
INSERT INTO #OverlappingDates
( UserID, date, starttime, endtime )
VALUES ( 1 -- UserID - int
, '20110203' -- date - date
, '09:30' -- starttime - time
, '09:35' -- endtime - time
),
( 1 -- UserID - int
, '20110203' -- date - date
, '09:31' -- starttime - time
, '09:38' -- endtime - time
),
( 1 -- UserID - int
, '20110203' -- date - date
, '10:03' -- starttime - time
, '10:05' -- endtime - time
),
( 2 -- UserID - int
, '20110203' -- date - date
, '11:02' -- starttime - time
, '11:05' -- endtime - time
),
( 2 -- UserID - int
, '20110203' -- date - date
, '11:05' -- starttime - time
, '11:15' -- endtime - time
),
( 2 -- UserID - int
, '20110203' -- date - date
, '11:05' -- starttime - time
, '12:00' -- endtime - time
);
WITH cte
AS ( SELECT UserID
, date
, MIN(starttime) AS StartTime
, MAX(endtime) AS EndTime
FROM #OverlappingDates
GROUP BY UserID
, date
, LEFT(starttime, 2)
, LEFT(endtime, 2)
)
SELECT cte.UserID
, cte.date
, cte.StartTime
, cte.EndTime
, ( RIGHT('0'
+ CAST(( DATEDIFF(SECOND,
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ', cte.StartTime) AS DATETIME) ),
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ', cte.EndTime) AS DATETIME) )) )
/ 3600 AS VARCHAR(2)), 2) + ':' + RIGHT('0'
+ CAST(( ( DATEDIFF(SECOND,
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ',
cte.StartTime) AS DATETIME) ),
( CAST(CONCAT(( CAST(cte.[date] AS VARCHAR(10)) ),
' ', cte.EndTime) AS DATETIME) )) )
/ 60 ) % 60 AS VARCHAR(2)),
2) ) AS Diff
FROM cte;