我知道有很多重叠的查询问题已经得到解答,但没有一个能够解决我遇到的问题:
我们需要找出最低start_date和最高end_date之间的总小时数,同时考虑重叠范围。
Start_Date End_Date
3/5/2010 11:27 3/5/2010 13:04 - Need to include
3/5/2010 11:29 3/5/2010 11:55 - Can exclude ( overlapping)
3/5/2010 13:13 3/5/2010 13:37 - Need to include
3/5/2010 13:13 3/5/2010 13:37 - Duplicate
3/5/2010 14:55 3/5/2010 15:22 - Need to include
3/5/2010 14:55 3/5/2010 15:22 - Duplicate
3/5/2010 15:15 3/5/2010 17:45 - Overlapping with above since it starts at 15.15, 7 minutes before the previous end_date
提前谢谢
约翰D
enter code here
答案 0 :(得分:1)
SELECT DATEDIFF(hour, MIN(Start_Date), MAX(End_Date)) AS [Diff]
FROM [table]
MSDN datediff
页面:http://msdn.microsoft.com/en-us/library/aa258269%28SQL.80%29.aspx
答案 1 :(得分:1)
这是一个有趣且有趣的问题。 1
让我们开始设置:
CREATE TABLE d (start_dt datetime, end_dt datetime)
INSERT INTO d VALUES ('3/5/2010 11:27', '3/5/2010 13:04')
INSERT INTO d VALUES ('3/5/2010 11:29', '3/5/2010 11:55')
INSERT INTO d VALUES ('3/5/2010 13:13', '3/5/2010 13:37')
INSERT INTO d VALUES ('3/5/2010 13:13', '3/5/2010 13:37')
INSERT INTO d VALUES ('3/5/2010 14:55', '3/5/2010 15:22')
INSERT INTO d VALUES ('3/5/2010 14:55', '3/5/2010 15:22')
INSERT INTO d VALUES ('3/5/2010 15:15', '3/5/2010 17:45')
这是通过TSQL实际计算它的代码:
-- Create a cursor that we will use to loop over all the items in the base table
DECLARE cur CURSOR FOR SELECT start_dt, end_dt FROM d
-- Temp variables to store each rows data
DECLARE @start_dt datetime, @end_dt datetime
-- Temp table to hold the "adjusted" rows
DECLARE @d TABLE (id int identity, start_dt datetime, end_dt datetime)
OPEN cur
FETCH NEXT FROM cur INTO @start_dt, @end_dt
WHILE @@FETCH_STATUS = 0
BEGIN
-- Start by deleting any rows contained entirely within the current rows timeframe
DELETE FROM @d WHERE start_dt BETWEEN @start_dt AND @end_dt AND end_dt BETWEEN @start_dt AND @end_dt
DECLARE @id_start_in int = -1, @id_end_in int = -1
SELECT @id_start_in = id FROM @d WHERE @start_dt BETWEEN start_dt AND end_dt
SELECT @id_end_in = id FROM @d WHERE @end_dt BETWEEN start_dt AND end_dt
-- If our start and end dates are not contained in any other set, add it as a new row
IF (@id_start_in = -1 AND @id_end_in = -1)
INSERT INTO @d (start_dt, end_dt) VALUES (@start_dt, @end_dt)
-- If our start date and end dates are both contained in the same row, ignore because we are overlapping that row
-- If our start date and end dates are in two different rows, we combine those two
-- For example if there are 3 rows, 1-3, 2-5, 4-6, we actually have full coverage from 1-6
IF (@id_start_in != @id_end_in AND @id_start_in != -1 AND @id_end_in != -1)
BEGIN
-- Expand the start row to end at the same time the row our end time is in
UPDATE @d SET end_dt = (SELECT end_dt FROM @d WHERE id = @id_end_in) WHERE id = @id_start_in
-- Delete the row our end time is in
DELETE FROM @d WHERE id = @id_end_in
END
-- If our start date is contained in a row but our end date isnt, extend the existing row
-- to end at our end date
IF (@id_start_in != -1 AND @id_end_in = -1)
UPDATE @d SET end_dt = @end_dt WHERE id = @id_start_in
-- If our end date is contained in a row but our start date isnt, extend the existing row
-- to start at our start time
IF (@id_start_in = -1 AND @id_end_in != -1)
UPDATE @d SET start_dt = @start_dt WHERE id = @id_end_in
FETCH NEXT FROM cur INTO @start_dt, @end_dt
END
CLOSE cur
DEALLOCATE cur
-- Show the end table
SELECT start_dt, end_dt, DATEDIFF(MINUTE, start_dt, end_dt) FROM @d
-- Sum up to get the minutes and calculate the hours
SELECT SUM(DATEDIFF(MINUTE, start_dt, end_dt)) AS MINUTES, CAST(SUM(DATEDIFF(MINUTE, start_dt, end_dt)) AS DECIMAL) / 60 AS HOURS FROM @d
答案 2 :(得分:0)
受到this answer about flattening date ranges的启发,我提出了这个问题:
-- Required function to flatten the time ranges
-- Assumes the end date is after the start.
CREATE FUNCTION Ranges(@startOfRange DATETIME, @endOfRange DATETIME)
RETURNS @rangesTable TABLE (
[start] DATETIME NOT NULL,
[end] DATETIME NOT NULL
)
AS
BEGIN
DECLARE @startTime DATETIME
DECLARE @endTime DATETIME
DECLARE @m_start DATETIME
DECLARE @m_end DATETIME
DECLARE cursorSpans CURSOR FAST_FORWARD FOR
SELECT DISTINCT [Start_Time], [End_Time]
FROM [YOUR_TABLE]
ORDER BY [Start_Time], [End_Time]
OPEN cursorSpans
FETCH NEXT FROM cursorSpans INTO @startTime, @endTime
SET @m_start = @startTime
SET @m_end = @endTime
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM cursorSpans INTO @startTime, @endTime
IF @startTime > @m_end
BEGIN
-- Only insert a new record if the current date range does not overlap the previous one.
INSERT INTO @rangesTable
VALUES(@m_start, @m_end)
SET @m_start = @startTime
END
SET @m_end = CASE WHEN @endTime > @m_end THEN @endTime ELSE @m_end END
END
-- Handle the final date range
IF @m_start IS NOT NULL
BEGIN
INSERT INTO @rangesTable
VALUES(@m_start, @m_end)
END
CLOSE cursorSpans
DEALLOCATE cursorSpans
RETURN
END
GO
-- Get the total minute difference between the start and end of each range, then total them up.
SELECT SUM(DATEDIFF(MINUTE, [Start], [End]))
FROM Ranges('2010-03-05 11:00:00', '2010-03-05 18:00:00')
GO
答案 3 :(得分:0)
使用Jeff Wright提供的表格:
CREATE TABLE d (start_dt datetime, end_dt datetime)
INSERT INTO d VALUES ('3/5/2010 11:27', '3/5/2010 13:04')
INSERT INTO d VALUES ('3/5/2010 11:29', '3/5/2010 11:55')
INSERT INTO d VALUES ('3/5/2010 13:13', '3/5/2010 13:37')
INSERT INTO d VALUES ('3/5/2010 13:13', '3/5/2010 13:37')
INSERT INTO d VALUES ('3/5/2010 14:55', '3/5/2010 15:22')
INSERT INTO d VALUES ('3/5/2010 14:55', '3/5/2010 15:22')
INSERT INTO d VALUES ('3/5/2010 15:15', '3/5/2010 17:45')
是时候享受Common Table Expressions:
with ranges as (
select d.start_dt,d.end_dt from d
union all
select r1.start_dt,r2.end_dt from d r1 inner join ranges r2 on r1.start_dt < r2.start_dt and r1.end_dt >= r2.start_dt
union all
select r1.start_dt,r2.end_dt from ranges r1 inner join d r2 on r2.end_dt > r1.end_dt and r2.start_dt <= r1.end_dt
), maxrange as (
select start_dt,MAX(end_dt) as end_dt from ranges group by start_dt
), minmaxrange as (
select MIN(start_dt) as start_dt,end_dt from maxrange group by end_dt
)
select SUM(DATEDIFF(MINUTE,start_dt,end_dt)) from minmaxrange
范围CTE查找所有重叠时段。然后,maxrange CTE找到特定start_dt的最新end_dt,minmaxrange找到特定end_dt的最早start_dt。这两者一起消除了重叠和重复。最后,我们以分钟为单位询问差异的总和。结果是291分钟。