2014-12-19 81 views
1

之间的时间,努力了解我们的日常调度问题一天之内的范围我一直在负责寻找差距。我看到关于这一主题的讨论很多,似乎充分确定的是游标通常要避免,这是一种耻辱,因为我知道如何做这些。识别时间差距日期项与起始和终止列

共识似乎像递归CTE是需要采取的方法,并且这些示例很丰富,但是基于与我的不同的源数据结构,通常跨越较长的时间段,并且看着“日”粒度而不是按分钟(我正在以15分钟的间隔工作,但这可能会改变)。下面是我一起工作的源数据的样本:

enter image description here

我的兴趣仅仅是在寻找在MIN(STARTTIME)和MAX(ENDTIME)之间的日程安排的空白,为这一天,写这些差距到另一张桌子。因为我有一些在上面描绘格式提供时查看数据相当快的查询,我想我有什么解决方案插入到补充表相同的格式。

一个努力我做:

SET NOCOUNT ON; 
USE tempdb; 
GO 

IF OBJECT_ID('#test', 'U') IS NOT NULL 
Drop table #test 

CREATE TABLE #test (
daterow int IDENTITY, 
obj_id int, 
datestart datetime, 
dateend datetime 
); 

INSERT INTO #test 
SELECT 
1, 
'2014-12-14 07:00:00', 
'2014-12-14 08:45:00' 
UNION 
SELECT 
1, 
'2014-12-14 09:00:00', 
'2014-12-14 09:45:00' 
UNION 
SELECT 
1, 
'2014-12-14 10:00:00', 
'2014-12-14 11:45:00' 
UNION 
SELECT 
1, 
'2014-12-14 12:00:00', 
'2014-12-14 14:45:00' 
UNION 
SELECT 
2, 
'2014-12-14 07:00:00', 
'2014-12-14 07:45:00' 
UNION 
SELECT 
2, 
'2014-12-14 08:00:00', 
'2014-12-14 10:45:00'; 


WITH C1 AS (
     SELECT obj_id, ts, Type 
      ,e=CASE Type WHEN 1 THEN NULL ELSE ROW_NUMBER() OVER (PARTITION BY obj_id, Type ORDER BY dateend) END 
      ,s=CASE Type WHEN -1 THEN NULL ELSE ROW_NUMBER() OVER (PARTITION BY obj_id, Type ORDER BY datestart) END 
     FROM #test 
     CROSS APPLY (
      VALUES (1, datestart), (-1, dateend)) a(Type, ts) 
     ), 
    C2 AS (
     SELECT C1.* 
      ,se=ROW_NUMBER() OVER (PARTITION BY obj_id ORDER BY ts, Type DESC) 
     FROM C1), 
    C3 AS (
     SELECT obj_id, ts 
      ,grpnm=FLOOR((ROW_NUMBER() OVER (PARTITION BY obj_id ORDER BY ts)-1)/2 + 1) 
     FROM C2 
     WHERE COALESCE(s-(se-s)-1, (se-e)-e) = 0), 
    -- C1, C2, C3, C4 combined remove the overlapping date periods 
    C4 AS (
     SELECT obj_id, datestart=MIN(ts), dateend=MAX(ts) 
     FROM C3 
     GROUP BY obj_id, grpnm) 
SELECT obj_id, datestart=MIN(newdate), dateend=MAX(newdate) 
FROM (
    SELECT obj_id, newdate 
     ,rn=ROW_NUMBER() OVER (PARTITION BY obj_id ORDER BY newdate)/2 
    FROM C4 a 
    CROSS APPLY (
     VALUES (datestart-1),(dateend+1)) b(newdate) 
    ) a 
GROUP BY obj_id, rn 
HAVING COUNT(*) = 2 
ORDER BY obj_id, datestart; 

导致在这个输出:

enter image description here

结果都是错误的,但我觉得它可能使在早上更有意义。也许有人可以揭示我的致命缺陷?

+0

可以添加预期输出 – 2014-12-19 09:33:39

+0

@ n8您正在使用什么版本的SQL Server? – 2014-12-19 09:35:47

+0

如果您可以想象使用游标的解决方案,然后使用它们。务实 - 游标可以是缓慢的和资源密集型的,但在某些情况下,它们肯定有一个地方,它们更难以以集合为导向解决。 – Rikalous 2014-12-19 09:37:13

回答

1

您的测试数据,并输出结果是不一样的,所以这使得它很难理解它是什么你要找的,但是,这是我的理解

使用您的测试数据...

obj_id  datestart   dateend 
1   2014-12-14 07:00:00.000 2014-12-14 08:45:00.000 
1   2014-12-14 09:00:00.000 2014-12-14 09:45:00.000 
1   2014-12-14 10:00:00.000 2014-12-14 11:45:00.000 
1   2014-12-14 12:00:00.000 2014-12-14 14:45:00.000 

有一个timelslot从8:45失踪了第1行,到9:00时,下次启动时是.. 所以,你会希望看到一个“时间差”开始在8:45至9 :00。下一行的9:45到10:00也是如此......等等。那是对的吗?

obj_id  datestart   dateend 
1   2014-12-14 07:00:00.000 2014-12-14 08:45:00.000 
1   2014-12-14 08:45:00.000 2014-12-14 09:00:00.000 <missing> 
1   2014-12-14 09:00:00.000 2014-12-14 09:45:00.000 
1   2014-12-14 09:45:00.000 2014-12-14 10:00:00.000 <missing> 
1   2014-12-14 10:00:00.000 2014-12-14 11:45:00.000 
1   2014-12-14 11:45:00.000 2014-12-14 12:00:00.000 <missing> 
1   2014-12-14 12:00:00.000 2014-12-14 14:45:00.000 

此查询...

SELECT a.obj_id, a.dateend as datestart, 
ISNULL(
    (SELECT TOP 1 c.datestart 
    FROM #test c 
    WHERE c.obj_id = a.obj_id 
    AND c.datestart > a.dateend 
    ORDER BY c.datestart), GETDATE()) dateend 
FROM #test a 
WHERE NOT EXISTS(
    SELECT NULL 
    FROM #test b 
    WHERE a.obj_id = b.obj_id 
    AND a.dateend = b.datestart) 
AND EXISTS(
SELECT NULL 
FROM #test c 
WHERE c.obj_id = a.obj_id 
AND c.datestart > a.dateend) 

产生以下结果....

obj_id  datestart   dateend 
1   2014-12-14 08:45:00.000 2014-12-14 09:00:00.000 
1   2014-12-14 09:45:00.000 2014-12-14 10:00:00.000 
1   2014-12-14 11:45:00.000 2014-12-14 12:00:00.000 

所有的测试数据丢失的插槽...(对于obj_id = 1)

希望帮助

+0

这适用于我的数据集,我可以将其修改为我的目的,而且速度非常快,谢谢! – 2014-12-19 17:53:15

2

如果你有机会到SQL Server 2012或更高版本,你可以使用LEAD

SQL Fiddle

MS SQL Server 2012的架构设置

CREATE TABLE test (
daterow int IDENTITY, 
obj_id int, 
datestart datetime, 
dateend datetime 
); 

INSERT INTO test 
SELECT 
1, 
'2014-12-14 07:00:00', 
'2014-12-14 08:45:00' 
UNION 
SELECT 
1, 
'2014-12-14 09:00:00', 
'2014-12-14 09:45:00' 
UNION 
SELECT 
1, 
'2014-12-14 10:00:00', 
'2014-12-14 11:45:00' 
UNION 
SELECT 
1, 
'2014-12-14 12:00:00', 
'2014-12-14 14:45:00' 
UNION 
SELECT 
2, 
'2014-12-14 07:00:00', 
'2014-12-14 07:45:00' 
UNION 
SELECT 
2, 
'2014-12-14 08:00:00', 
'2014-12-14 10:45:00'; 

查询1

DECLARE @EndDate DATETIME = '20141215 00:00:00' 
DECLARE @StartDate DATETIME = '20141214' 
;WITH gaps 
AS 
(
    SELECT T.obj_id, 
      T.DateEnd As DateStart, 
      LEAD(T.DATESTART, 1, @EndDate) OVER (PARTITION BY T.obj_id ORDER BY T.DateRow) AS DateEnd 
    FROM TEST T 

), 
minStart 
AS 
(
    SELECT obj_id, MIN(@StartDate) As DateStart, MIN(DateStart) AS DateEnd 
    FROM TEST 
    GROUP BY obj_id 
    HAVING MIN(@StartDate) < MIN(DateStart) 
) 
SELECT obj_id, DateStart, DateEnd 
FROM gaps 
WHERE DATEDIFF(mi, DateStart, DateEnd) > 0 
UNION 
SELECT obj_id, DateStart, DateEnd 
FROM minStart 
ORDER BY obj_id, DateStart 

Results

| OBJ_ID |      DATESTART |       DATEEND | 
|--------|---------------------------------|---------------------------------| 
|  1 | December, 14 2014 00:00:00+0000 | December, 14 2014 07:00:00+0000 | 
|  1 | December, 14 2014 08:45:00+0000 | December, 14 2014 09:00:00+0000 | 
|  1 | December, 14 2014 09:45:00+0000 | December, 14 2014 10:00:00+0000 | 
|  1 | December, 14 2014 11:45:00+0000 | December, 14 2014 12:00:00+0000 | 
|  1 | December, 14 2014 14:45:00+0000 | December, 15 2014 00:00:00+0000 | 
|  2 | December, 14 2014 00:00:00+0000 | December, 14 2014 07:00:00+0000 | 
|  2 | December, 14 2014 07:45:00+0000 | December, 14 2014 08:00:00+0000 | 
|  2 | December, 14 2014 10:45:00+0000 | December, 15 2014 00:00:00+0000 | 
+0

可悲的是我没有2012年,但感谢你的洞察力。 – 2014-12-19 17:15:16