问题
I have two tables T1 and T2 with start and end fields.
What I want is : the parts of T2 that are not in T1.
The Drawing
T1 : [----][----] [-----]
T2 : [---------------] [------------]
R : [-] [--] [--] [---]
R here is the result.
Data
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
I use SQL Server (2012 and more) and the type of my fields are DateTime2.
My main issue here is the first case in my drawing => When you have 2 or more intervals covered by one.
Thanks a lot for your time.
回答1:
So for the guys that didn't understand the problem only by the drawing I will add a drawing here with the OP's problem with the data provided.
This is a kind of problem that you only understand if you already dealt with.
dt2 dt3|dt4 dt5 dt8 dt9
T1 : [-----]|[-----] [---------]
dt1 dt6 dt7 dt10
T2 : [--------------------------] [--------------------------]
R1s R1e R2s R2e R3s R3e R4s R4e
R : [----] [-------] [--------] [-------]
The labels means:
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
...
They are periods of date and time. Note that the |
between Date 3 and 4 is just to show that there is no interval between then.
My Solution
For this type of problem, the very first thing you have to do is to know all your periods starts and ends as one so I created a VIEW
with it as I will use it more than once on the final select (if you wish, you don't need to create the view just use the query as subquery)
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;
This view will give me all dates (starts and ends) in just one field dtperiod
I will also need another view to to UNION all starts
and ends
from both T1
and T2
so
create or replace view vw_times2 as
select dtstart, dtend from t1 UNION
select dtstart, dtend from t2;
So the final query will be:
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;
Remember that I'm using the views I created on this query so it can be more readable.
There's on thing worth to mention on this query. Since you only want as result the missing periods for each day, I've added this filter AND DAY(t.dtstart) = DAY(t.dtend)
so the query won't give you missing periods between days. In your sample set it would be 2015-05-14 23:00:00 2015-05-16 12:00:00
and 2015-05-16 14:00:00 NULL
last one because of the period tweak on my query.
Here is the working solution on SQLFiddle: http://sqlfiddle.com/#!9/6a8a9/1
Note that I created it (sqlfiddle) using MySql (because it is unstable with sqlserver). Since it only use plain sql it will work as the same on SQLServer (the only difference is day extraction. Here on the answer I added the sqlserver version).
回答2:
Answer I came out with:
--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;
来源:https://stackoverflow.com/questions/34138553/date-difference-between-two-tables