我的任务是找出当天时间范围之间的差距,以便了解我们的日常安排问题。我看到很多关于这个主题的讨论,似乎已经确定游标通常是要避免的,这是一种耻辱,因为我理解如何做到这些。
共识似乎是递归CTE是采取的方法,这些的例子很丰富,但是基于不同的源数据结构而不是我的,通常跨越更长的时间段并且看着" day"粒度而不是分钟(我每隔15分钟工作一次,但这可能会有变化)。以下是我正在使用的源数据示例:
我的兴趣仅在于找到当天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;
导致此输出结果:
结果都错了,但我觉得早上可能更有意义。也许有人可以揭露我的致命缺陷?
答案 0 :(得分:2)
如果您有权访问SQL Server 2012或更高版本,则可以使用LEAD
:
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)
希望有所帮助