使用START和END列标识日期条目中的时间间隔

时间:2014-12-19 09:28:27

标签: sql-server sql-server-2008

我的任务是找出当天时间范围之间的差距,以便了解我们的日常安排问题。我看到很多关于这个主题的讨论,似乎已经确定游标通常是要避免的,这是一种耻辱,因为我理解如何做到这些。

共识似乎是递归CTE是采取的方法,这些的例子很丰富,但是基于不同的源数据结构而不是我的,通常跨越更长的时间段并且看着" day"粒度而不是分钟(我每隔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

结果都错了,但我觉得早上可能更有意义。也许有人可以揭露我的致命缺陷?

2 个答案:

答案 0 :(得分: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 |

答案 1 :(得分: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

从下一个开始时间的8:45到9:00,第1行缺少时间段。 所以你会期望看到一个&#34;时间差距&#34;从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)

希望有所帮助