This seems to work and keep the precision as well:
SELECT DATEADD(day, DATEDIFF(day,'19000101',@D), CAST(@T AS DATETIME2(7)))
The CAST
to DATETIME2(7)
converts the TIME(7)
value (@T
) to a DATETIME2
where the date part is '1900-01-01'
, which is the default value of date and datetime types (see datetime2
and the comment* at CAST
and CONVERT
page at MSDN.)
* ... When character data that represents only date or only time components is cast to the datetime or smalldatetime data types, the unspecified time component is set to 00:00:00.000, and the unspecified date component is set to 1900-01-01.
The DATEADD()
and DATEDIFF()
function take care of the rest, i.e. adding the difference in days between the 1900-01-01
and the DATE
value (@D
).
Test at: SQL-Fiddle
As noticed by @Quandary, the above expression is considered not-determninistic by SQL Server. If we want a deterministic expression, say because it is to be used for a PERSISTED
column, the '19000101'
** needs to be replaced by 0
or CONVERT(DATE, '19000101', 112)
:
CREATE TABLE date_time
( d DATE NOT NULL,
t TIME(7) NOT NULL,
dt AS DATEADD(day,
DATEDIFF(day, CONVERT(DATE, '19000101', 112), d),
CAST(t AS DATETIME2(7))
) PERSISTED
) ;
**: DATEDIFF(day, '19000101', d)
isn't deterministic as it does an implicit conversion of the string to DATETIME
and conversions from strings to datetime are deterministic only when specific styles are used.
THIS IS THE QUERY YOU NEED
SET @GivenDate = '2014-04-15';
SELECT CONCAT(@GivenDate,' ',MAX(time)) INTO @LastDateTime
FROM airport_taxi WHERE date=@GivenDate;
SET @nexttime = 0;
SELECT
TIME(FROM_UNIXTIME(StartTime)) "start",
TIME(FROM_UNIXTIME(EndTime)) "end",
FLOOR((EndTime - StartTime)/60) diff_min
FROM (SELECT (@prevtime:=@nexttime) StartTime,(@nexttime:=dttm) EndTime
FROM (SELECT FLOOR(UNIX_TIMESTAMP(CONCAT(date,' ',time))) dttm
FROM airport_taxi WHERE date=@GivenDate ORDER BY time) AA) A WHERE StartTime>0
UNION SELECT
TIME(@LastDateTime),TIME(DATE(@LastDateTime+INTERVAL 1 DAY) + INTERVAL 0 SECOND),
FLOOR((UNIX_TIMESTAMP(DATE(@LastDateTime+INTERVAL 1 DAY) + INTERVAL 0 SECOND) -
UNIX_TIMESTAMP(@LastDateTime))/60)
;
SAMPLE DATA
mysql> use test
Database changed
mysql> drop table if exists airport_taxi;
Query OK, 0 rows affected (0.13 sec)
mysql> create table airport_taxi
-> (id int not null auto_increment primary key,
-> date date,time time,direction varchar(10));
Query OK, 0 rows affected (0.38 sec)
mysql> insert into airport_taxi values
-> ( 7,'2014-04-15','21:35:00','to'),
-> ( 8,'2014-04-15','15:20:00','from'),
-> ( 9,'2014-04-15','17:35:00','to'),
-> (10,'2014-04-15','08:15:00','to'),
-> (11,'2014-04-15','14:40:00','to'),
-> (12,'2014-04-15','11:35:00','to'),
-> (13,'2014-04-15','14:35:00','from');
Query OK, 7 rows affected (0.21 sec)
Records: 7 Duplicates: 0 Warnings: 0
mysql> select * from airport_taxi;
+----+------------+----------+-----------+
| id | date | time | direction |
+----+------------+----------+-----------+
| 7 | 2014-04-15 | 21:35:00 | to |
| 8 | 2014-04-15 | 15:20:00 | from |
| 9 | 2014-04-15 | 17:35:00 | to |
| 10 | 2014-04-15 | 08:15:00 | to |
| 11 | 2014-04-15 | 14:40:00 | to |
| 12 | 2014-04-15 | 11:35:00 | to |
| 13 | 2014-04-15 | 14:35:00 | from |
+----+------------+----------+-----------+
7 rows in set (0.00 sec)
QUERY EXECUTED
mysql> SET @GivenDate = '2014-04-15';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT CONCAT(@GivenDate,' ',MAX(time)) INTO @LastDateTime
-> FROM airport_taxi WHERE date=@GivenDate;
Query OK, 1 row affected (0.00 sec)
mysql> SET @nexttime = 0;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT
-> TIME(FROM_UNIXTIME(StartTime)) "start",
-> TIME(FROM_UNIXTIME(EndTime)) "end",
-> FLOOR((EndTime - StartTime)/60) diff_min
-> FROM (SELECT (@prevtime:=@nexttime) StartTime,(@nexttime:=dttm) EndTime
-> FROM (SELECT FLOOR(UNIX_TIMESTAMP(CONCAT(date,' ',time))) dttm
-> FROM airport_taxi WHERE date=@GivenDate ORDER BY time) AA) A WHERE StartTime>0
-> UNION SELECT
-> TIME(@LastDateTime),TIME(DATE(@LastDateTime+INTERVAL 1 DAY) + INTERVAL 0 SECOND),
-> FLOOR((UNIX_TIMESTAMP(DATE(@LastDateTime+INTERVAL 1 DAY) + INTERVAL 0 SECOND) -
-> UNIX_TIMESTAMP(@LastDateTime))/60)
-> ;
+----------+----------+----------+
| start | end | diff_min |
+----------+----------+----------+
| 08:15:00 | 11:35:00 | 200 |
| 11:35:00 | 14:35:00 | 180 |
| 14:35:00 | 14:40:00 | 5 |
| 14:40:00 | 15:20:00 | 40 |
| 15:20:00 | 17:35:00 | 135 |
| 17:35:00 | 21:35:00 | 240 |
| 21:35:00 | 00:00:00 | 145 |
+----------+----------+----------+
7 rows in set (0.00 sec)
mysql>
Just remember to set the @GivenDate to whatever date you need
BTW Your last line is based on midnight the next day. Therefore, the diff_min
is 145 not 25. If you have a knock-off time of 10:00 PM at th eairport, then change
UNION SELECT
TIME(@LastDateTime),TIME(DATE(@LastDateTime + INTERVAL 1 DAY) + INTERVAL 0 SECOND),
FLOOR((UNIX_TIMESTAMP(DATE(@LastDateTime + INTERVAL 1 DAY) + INTERVAL 0 SECOND) -
UNIX_TIMESTAMP(@LastDateTime))/60)
to this
UNION SELECT
TIME(@LastDateTime),TIME(DATE(@LastDateTime + INTERVAL 1 DAY) + INTERVAL 0 SECOND),
FLOOR((UNIX_TIMESTAMP(DATE(@LastDateTime + INTERVAL 1 DAY) + INTERVAL 0 SECOND) -
UNIX_TIMESTAMP(@LastDateTime))/60) - 120
Subtracting 120 backs 2 hours away from midnight of the next day makes the last line's end 10:00 PM
Give it a Try !!!
UPDATE 2014-04-16 09:44 EDT
I cannot really test this, but you can try this:
SET @GivenDate = '2014-04-15';
SELECT CONCAT(@GivenDate,' ',MAX(time)) INTO @LastDateTime
FROM airport_taxi WHERE date=@GivenDate;
SELECT id INTO @LastID FROM airport_taxi
WHERE date=@GivenDate ORDER BY time DESC LIMIT 1;
SET @nexttime = 0;
SELECT
id,
TIME(FROM_UNIXTIME(StartTime)) "start",
TIME(FROM_UNIXTIME(EndTime)) "end",
FLOOR((EndTime - StartTime)/60) diff_min
FROM (SELECT id,(@prevtime:=@nexttime) StartTime,(@nexttime:=dttm) EndTime
FROM (SELECT id,FLOOR(UNIX_TIMESTAMP(CONCAT(date,' ',time))) dttm
FROM airport_taxi WHERE date=@GivenDate ORDER BY time) AA) A WHERE StartTime>0
UNION SELECT @LastID,
TIME(@LastDateTime),TIME(DATE(@LastDateTime+INTERVAL 1 DAY) + INTERVAL 0 SECOND),
FLOOR((UNIX_TIMESTAMP(DATE(@LastDateTime+INTERVAL 1 DAY) + INTERVAL 0 SECOND) -
UNIX_TIMESTAMP(@LastDateTime))/60)
;
I hope this helps !!!
Best Answer
I'd do this using a subquery to combine the date and time, then round down before aggregating the data. This way, it will include the '00:00' hour into the previous day which will prevent you from having to searching for two dates:
Here is an example of it in action: sqlfiddle.