2015-12-07 269 views
0


我有两个表T1和T2带有开始和结束字段。
我想要的是:T2中不在T1中的部分。两个表之间的日期差异

附图

T1 : [----][----]   [-----] 
T2 : [---------------] [------------] 
R : [-]   [--] [--]  [---] 

这里R是的结果。

数据

T1 : 2015-05-14 07:00:00 2015-05-14 14:00:00 2015-05-14 14:00:00 2015-05-14 19:00:00 2015-05-16 12:30:00 2015-05-16 13:30:00

T2 : 2015-05-14 05:00:00 2015-05-14 23:00:00 2015-05-16 12:00:00 2015-05-16 14:00:00

R : 2015-05-14 05:00:00 2015-05-14 07:00:00 2015-05-14 19:00:00 2015-05-14 23:00:00 2015-05-16 12:00:00 2015-05-16 12:30:00 2015-05-16 13:30:00 2015-05-16 14:00:00

我使用SQL Server(2012多),我的字段的类型是DATETIME2。

这里我的主要问题是我的绘图中的第一个情况=>当你有一个覆盖2个或更多的区间。
非常感谢您的时间。

+3

图纸不会帮助你的事业。发布实际数据 –

+1

指定您的RDBMS(SQL * flavor *),因为实现中的日期/时间函数不同。 –

+0

@vkp其实它和日期有关。不需要更多信息。 –

回答

1

因此,对于未通过只绘制我会加理解这个问题的家伙与所提供的数据业务方案的问题画在这里。

这是一种只有在你已经处理过的情况下才能理解的问题。

  dt2 dt3|dt4 dt5     dt8  dt9 
T1 :  [-----]|[-----]      [---------] 
     dt1      dt6 dt7      dt10 
T2 : [--------------------------] [--------------------------] 
     R1s R1e   R2s  R2e R3s  R3e  R4s  R4e 
R : [----]    [-------] [--------]   [-------] 

标签是指:

dt1 - Date 1 
dt2 - Date 2 
.... 
dt10 - Date 10 
------- 
R1s - Result date start 1 
R1e - Result date end 1 
R2s - Result date start 2 
R2e - Result date end 2 
... 

他们的日期和时间的时期。请注意,日期3和4之间的|只是表明在此之间没有间隔。

我的解决方案

对于这类问题,你要做的第一件事就是要知道你所有的时间开始两端为一体的,所以我用它创建了一个VIEW我将不止一次使用它的最终选择(如果你愿意,你不需要创建视图只是使用查询作为子查询)

create or replace view vw_times as 
    select dtstart as dtperiod from t1 UNION 
    select dtend from t1 UNION 
    select dtstart from t2 UNION 
    select dtend from t2; 

这种观点会给我所有的时间(开始和结束)只在一个领域dtperiod

我还需要另一种观点认为对UNION ALL来自T1startsendsT2所以

create or replace view vw_times2 as 
    select dtstart, dtend from t1 UNION 
    select dtstart, dtend from t2; 

所以最终的查询将是:

SELECT t.dtstart, t.dtend 
    FROM (
     SELECT t1.dtperiod as dtstart, 
       (SELECT min(dtperiod) second 
        FROM vw_times x where x.dtperiod > t1.dtperiod) as dtend 
      FROM vw_times t1 
        LEFT JOIN (SELECT (dtperiod - interval 1 second) dtperiod 
           FROM vw_times) t2 
          ON (t1.dtperiod = t2.dtperiod) 
     WHERE t2.dtperiod is null 
     ) t LEFT JOIN vw_times2 t2 ON ( t.dtstart = t2.dtstart 
             AND t.dtend = t2.dtend) 
WHERE t2.dtstart IS NULL 
    AND DAY(t.dtstart) = DAY(t.dtend) 
ORDER BY t.dtstart; 

请记住,我正在使用我在此查询中创建的视图,以便它更具可读性。

在这个查询上值得一提。由于您只希望每天的缺失时间段结束,因此我添加了此筛选器AND DAY(t.dtstart) = DAY(t.dtend),因此查询不会让您在几天之间遗漏时间段。在你的样本集中,因为查询期间的调整,它将是2015-05-14 23:00:00 2015-05-16 12:00:002015-05-16 14:00:00 NULL最后一个。

这里是SQLFiddle工作的解决方案:使用MySQL http://sqlfiddle.com/#!9/6a8a9/1

注意,我创建它(sqlfiddle)(因为它与SQLSERVER不稳定)。由于它只使用普通的sql,它将在SQLServer上同样工作(唯一的不同是日提取,这里的答案我添加了sqlserver版本)。

+0

这是为我工作,感谢您的解释和时间! – Minouz

0

回答我走了出来:

--data init 
DECLARE @t1 AS TABLE (db DATETIME ,de DATETIME ); 
DECLARE @t2 AS TABLE (db DATETIME ,de DATETIME ); 

INSERT INTO @t1 (db , de )VALUES ('2015-05-14 07:00:00.000' , '2015-05-14 14:00:00.000' ); 
INSERT INTO @t1 (db , de )VALUES ('2015-05-14 14:00:00' , '2015-05-14 19:00:00' ); 
INSERT INTO @t1 (db , de )VALUES ('2015-05-16 12:30:00' , '2015-05-16 13:30:00' ); 

INSERT INTO @t2 (db , de )VALUES ('2015-05-14 05:00:00' , '2015-05-14 23:00:00' ); 
INSERT INTO @t2 (db , de )VALUES ('2015-05-16 12:00:00' , '2015-05-16 14:00:00' ) 

--actual answer 
;WITH cte 
    AS (SELECT * , 
ROW_NUMBER() OVER (PARTITION BY db ORDER BY de) num , 
ROW_NUMBER() OVER (PARTITION BY de ORDER BY db DESC) num2 
    FROM  (SELECT t2.db , 
t.db AS de 
    FROM @t2 t2 
JOIN @t1 t ON t.db BETWEEN t2.db AND t2.de 
    AND t.de BETWEEN t2.db AND t2.de 
    UNION 
    SELECT t.de AS db , 
t2.de 
    FROM @t2 t2 
JOIN @t1 t ON t.db BETWEEN t2.db AND t2.de 
    AND t.de BETWEEN t2.db AND t2.de 
) zzz 
) 
    SELECT * 
    FROM cte 
    WHERE num = 1 
AND num2 = 1;