MYSQL check if a chain of dates exists - mysql

I want to check in MySQL if a chain of dates exists longer than 5. For example:
11-05-2016
12-05-2016
13-05-2016
14-05-2016
15-05-2016
These would be rows in my DB, this would be a chain of 5. I want to make a trigger on my table to protect my data from making chains bigger than 5.

Here's an incomplete answer...
I ain't pretending this is pretty (and it scales poorly too), but this does at least demonstrate a principle that others might build off...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table(dt DATE NOT NULL PRIMARY KEY);
INSERT INTO my_table VALUES
('2016-05-01'),
('2016-05-02'),
('2016-05-03'),
('2016-05-05'),
('2016-05-07'),
('2016-05-08'),
('2016-05-09'),
('2016-05-11'),
('2016-05-12'),
('2016-05-13'),
('2016-05-14');
Here we have a streak of 3 days (1st to 3rd), followed by a streak of 1 day (5th), followed by another streak of 3 days (7th to 9th), followed by a 4 day streak (11th to 14th).
So, we should be able to insert a value of '2016-05-15' (because that would produce a 5-day streak), but not '2016-05-10' (because that would make an 8-day streak).
1. 2016-05-10
SET #x = '2016-05-10';
SELECT a.dt start
, MIN(c.dt) end
FROM
( SELECT dt FROM my_table
UNION
SELECT #x
) a
LEFT
JOIN
( SELECT dt FROM my_table
UNION
SELECT #x
) b
ON b.dt = a.dt - INTERVAL 1 DAY
LEFT
JOIN
( SELECT dt FROM my_table
UNION
SELECT #x
) c
ON c.dt >= a.dt
LEFT
JOIN
( SELECT dt FROM my_table
UNION
SELECT #x
) d
ON d.dt = c.dt + INTERVAL 1 DAY
WHERE b.dt IS NULL
AND c.dt IS NOT NULL
AND d.dt IS NULL
GROUP
BY a.dt
HAVING DATEDIFF(end,start)>5;
+------------+------------+
| start | end |
+------------+------------+
| 2016-05-07 | 2016-05-15 |
+------------+------------+
2. 2016-05-15
SET #x = '2016-05-15';
SELECT a.dt start
, MIN(c.dt) end
FROM
( SELECT dt FROM my_table
UNION
SELECT #x
) a
LEFT
JOIN
( SELECT dt FROM my_table
UNION
SELECT #x
) b
ON b.dt = a.dt - INTERVAL 1 DAY
LEFT
JOIN
( SELECT dt FROM my_table
UNION
SELECT #x
) c
ON c.dt >= a.dt
LEFT
JOIN
( SELECT dt FROM my_table
UNION
SELECT #x
) d
ON d.dt = c.dt + INTERVAL 1 DAY
WHERE b.dt IS NULL
AND c.dt IS NOT NULL
AND d.dt IS NULL
GROUP
BY a.dt
HAVING DATEDIFF(end,start)>5;
Empty set (0.03 sec)

Related

Mysql Select Query to find items also partially free between two given dates

I am in need of listing free items between two given dates.
I have in table A items and in table B their occupancies.
When I search for the free items between two dates, I need to list also items partially free.
Tabel A:
| ID | ITEM_NAME |
| -------- | -------------- |
| 1 | Item1 |
| 2 | Item2 |
| 3 | Item3 |
Table B:
|id_item |occupancy_start_date |occupancy_end_date|
|---------------------------------------------------------|
|1 |2021-07-24 |2021-08-06 |
|2 |2021-07-24 |2021-07-31 |
|3 |2021-07-29 |2021-08-03 |
While I search for free items between 2021-07-24 and 2021-08-06, I must get Item2 and Item3.
Item2 is free from 2021-08-01 till 2021-08-06
Item3 is free from 2021-07-24 till 2021-07-29
Item3 is free from 2021-08-04 till 2021-08-06
(Practically I must find free slots of dates between two given dates by the user)
Can you guys help me? Thank you.
Here's another starting point (using the fiddle generously provided by others - pushed up to a more recent version) ... I'll post a more complete answer next week, if no one's beaten me to it...
WITH RECURSIVE cte AS (
SELECT '2021-07-24' dt
UNION ALL
SELECT dt + INTERVAL 1 DAY
FROM cte
WHERE dt < '2021-08-06')
SELECT x.dt
, a.*
FROM cte x
JOIN TableA a
LEFT
JOIN TableB b
ON b.id_item = a.id
AND x.dt BETWEEN b.occupancy_start_date AND b.occupancy_end_date
WHERE b.id_item IS NULL
Without window function :
There is a way to extract by dividing into three parts: "From start date", "Till end date" and "Others", and filter the rows by each query, like this:
SET #sdate = DATE'2021-07-24';
SET #edate = DATE'2021-08-06';
-- From start date
SELECT
b.id_item id_item,
#sdate from_date,
DATE_ADD(MIN(b.occupancy_start_date), INTERVAL -1 DAY) to_date
FROM TableB b
GROUP BY b.id_item
HAVING from_date <= to_date
-- Till end date
UNION ALL
SELECT
b.id_item id_item,
DATE_ADD(MAX(b.occupancy_end_date), INTERVAL 1 DAY) from_date,
#edate to_date
FROM TableB b
GROUP BY b.id_item
HAVING from_date <= to_date
-- Others
UNION ALL
SELECT
b1.id_item,
DATE_ADD(b1.occupancy_end_date, INTERVAL 1 DAY),
DATE_ADD(b2.occupancy_start_date, INTERVAL -1 DAY)
FROM TableB b1 INNER JOIN TableB b2
ON b1.id_item=b2.id_item AND
b1.occupancy_end_date < b2.occupancy_start_date AND
-- Exclude inappropriate rows
NOT EXISTS (SELECT 1 FROM TableB b WHERE
b1.id_item = b.id_item AND (
( b1.occupancy_end_date < b.occupancy_start_date AND
b.occupancy_start_date < b2.occupancy_start_date) OR
( b1.occupancy_end_date < b.occupancy_end_date AND
b.occupancy_end_date < b2.occupancy_start_date) ) )
ORDER BY 1,2
;
DB Fiddle
With window function :
If MySQL8, you can use the LAG (or LEAD) function, like this:
SET #sdate = DATE'2021-07-24';
SET #edate = DATE'2021-08-06';
SELECT
a.ITEM_NAME,
w.id_item,
w.from_date,
w.to_date
FROM (
-- Non-free period(s) exist
SELECT * FROM (
SELECT
id_item,
DATE_ADD(LAG(occupancy_end_date, 1)
OVER (PARTITION BY id_item ORDER BY occupancy_end_date),
INTERVAL 1 DAY) from_date,
DATE_ADD(occupancy_start_date, INTERVAL -1 DAY) to_date
FROM TableB
WHERE #sdate < occupancy_end_date AND occupancy_start_date < #edate
) t
WHERE from_date <= to_date
-- From start date
UNION ALL
SELECT
id_item,
#sdate from_date,
DATE_ADD(MIN(occupancy_start_date), INTERVAL -1 DAY) to_date
FROM TableB
GROUP BY id_item
HAVING from_date <= to_date
-- Till end date
UNION ALL
SELECT
id_item,
DATE_ADD(MAX(occupancy_end_date), INTERVAL 1 DAY) from_date,
#edate to_date
FROM TableB
GROUP BY id_item
HAVING from_date <= to_date
-- No occupancy
UNION ALL
SELECT
ID,
#sdate from_date,
#edate to_date
FROM TableA a
WHERE NOT EXISTS (SELECT 1 FROM TableB b WHERE a.ID=b.id_item)
) w
INNER JOIN TableA a ON w.id_item=a.ID
ORDER BY
w.id_item,
w.from_date
;
DB Fiddle

Select and count data per day [duplicate]

This question already has answers here:
generate days from date range
(30 answers)
Closed 3 years ago.
I have a following table with columns:
id | number | created_at
1 | A11 | 2020-01-01 06:08:19
2 | A21 | 2020-01-04 06:08:19
How do I query all the data in a date range from specific date and count all data per day?
I tried something like that :
SELECT DATE_FORMAT(created_at, '%Y-%m-%d') AS the_date , COUNT(*) AS count
FROM `transactions`
WHERE created_at BETWEEN DATE_FORMAT('2020-01-01', '%Y-%m-%d') AND DATE_FORMAT('2020-01-04', '%Y-%m-%d')
GROUP BY the_date
Then i got data like that :
the_date | count
2020-01-01 | 1
2020-01-04 | 1
I want to achieve
the_date | count
2020-01-01 | 1
2020-01-02 | 0
2020-01-03 | 0
2020-01-04 | 1
if your version is below mysql 8.0 then you can use this script :
step1 : create a sequence N rows table :
create table sequence(id int);
create procedure insert_data_proc(in v_i int)
begin
declare i int default 0;
while i < v_i
do
insert into sequence values (i);
set i = i + 1;
end while;
end;
call insert_data_proc(1000);
drop procedure insert_data_proc;
step2 : query the table and left join your table's by mindate,maxdate,datediff
select
t1.created_at the_date
,case when count is null then 0 else count end as count
from (
select date_add(t2.mincreated_at , interval id day) created_at
from sequence t1
left join (
select datediff(max(created_at),min(created_at)) diff
,date(min(created_at) ) mincreated_at
,date(max(created_at) ) maxcreated_at
from transactions
) t2 on 1=1
where t1.id < t2.diff+1
) t1
left join (
select date(created_at) created_at,count(1) count
from transactions
group by date(created_at)
) t2 on t1.created_at = t2.created_at
order by the_date
note : if your data's days over 1000 day then you only need to increase the SP value.
[Online Demo Link MySQL 5.7 | db<>fiddle](https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=57d3e60bb2b918e8b6d2d8f3d5e63a6c
)
you can use like this :
SET #date_min = '2019-01-01';
SET #date_max = '2019-01-04';
SELECT
date_generator.date as the_date,
IFNULL(COUNT(transactions.id), 0) as count
from (
select DATE_ADD(#date_min, INTERVAL (#i:=#i+1)-1 DAY) as `date`
from information_schema.columns,(SELECT #i:=0) gen_sub
where DATE_ADD(#date_min,INTERVAL #i DAY) BETWEEN #date_min AND #date_max
) date_generator
left join transactions on DATE(created_at) = date_generator.date
GROUP BY date;
so here I am creating a temporary table date_generator will dates in between of given date range and join to with your main table (transactions).
output as expected:
the_date | count
2020-01-01 | 1
2020-01-02 | 0
2020-01-03 | 0
2020-01-04 | 1
I will give a suggestion for you to do this,
1 Solution
Create temporary table and add the dates and then join with the transactions table
create temporary table tempcalander
as
select * from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) dates from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where dates between '2020-01-01' and '2020-01-04';
SELECT DATE_FORMAT(dates, '%Y-%m-%d') AS the_date , COUNT(created_at) AS count
FROM transactions t right join tempcalander c on t.created_at = c.dates
WHERE dates BETWEEN DATE_FORMAT('2020-01-01', '%Y-%m-%d') AND DATE_FORMAT('2020-01-04', '%Y-%m-%d')
GROUP BY the_date
2 Solution
you can create a separate table to add your dates.
CREATE TABLE calendar
(
dates date PRIMARY KEY
) ;
Then add you dates to this table,
INSERT INTO
calendar (dates)
VALUES
('2020-01-01'),
('2020-01-02'),
('2020-01-03'),
('2020-01-04'),
('2020-01-05'),
('2020-01-06') ;
after you can join the the transactions table with the calendar table and get the output
SELECT DATE_FORMAT(dates, '%Y-%m-%d') AS the_date , COUNT(created_at) AS count
FROM transactions t right join calendar c on t.created_at = c.dates
WHERE dates BETWEEN DATE_FORMAT('2020-01-01', '%Y-%m-%d') AND DATE_FORMAT('2020-01-04', '%Y-%m-%d')
GROUP BY the_date

SQL generate free slots on the fly from bookings table?

I know there's a lot of booking questions on php section.
Belive me when i tell you that I tried most of them, at least those which seems compatible. So let me explain.
I have this appointments table
ID | day | start | end |
----------------------------------
1 | 01-01-2018 | 09:00 | 10:00 |
2 | 01-01-2018 | 10:00 | 13:00 |
3 | 02-01-2018 | 12:00 | 15:00 |
4 | 02-01-2018 | 18:00 | 19:30 |
I was wondering, it possibile with sql to get empty time slots? The result should like something like:
day | start | end
---------------------------
01-01-2018 | 00:00 | 09:00
01-01-2018 | 13:00 | 23:59
02-01-2018 | 00:00 | 12:00
02-01-2018 | 15:00 | 18:00
02-01-2018 | 19:30 | 23:59
The query should contain 2 dates: start_day + end_day
I prepared a fiddle here: https://www.db-fiddle.com/f/6dm8q8UtmDkkkjExYfMEbx/1
MSSQL Version
WITH X AS
(
SELECT ROW_NUMBER() OVER (ORDER BY Day, Start)sq, [Day], [Start], [End]
FROM (
SELECT [Day], [Start], [End]
FROM [appointments]
UNION
SELECT Day, '00:00', '00:00'
FROM [appointments]
UNION
SELECT Day, '23:59', '23:59'
FROM [appointments]
) T1
)
SELECT A.Day, A.[End] AS Start, b.[Start] AS End
FROM x A
JOIN x B
ON A.sq = B.sq -1
AND A.[Day] = B.[Day]
AND A.[End] <> b.[Start]
Mysql 5.7 version
SET #RowNumber = 0;
CREATE TABLE cte
SELECT (#RowNumber := #RowNumber+1) AS Rownumber, Day, Start, End
FROM (
SELECT Day, Start, End
FROM booking
UNION
SELECT Day, '00:00', '00:00'
FROM booking
UNION
SELECT Day, '23:59', '23:59'
FROM booking
) T1
ORDER BY day ASC, Start ASC
;
SELECT A.Day, A.End AS Start, B.Start AS End
FROM cte A
JOIN cte B
ON A.Rownumber = B.Rownumber -1
AND A.Day = B.Day
AND A.End <> B.Start
ORDER BY A.Day asc, A.End asc
Will add a fiddle to demonstrate
https://www.db-fiddle.com/f/6dm8q8UtmDkkkjExYfMEbx/2
Mysql 5.7 including days without bookings
SET #RowNumber = 0;
CREATE TABLE cte
SELECT (#RowNumber := #RowNumber+1) AS Rownumber, Day, Start, End
FROM (
SELECT Day, Start, End
FROM booking
UNION
SELECT Day, '00:00', '00:00'
FROM booking
UNION
SELECT Day, '23:59', '23:59'
FROM booking
) T1
ORDER BY day ASC, Start ASC
;
SELECT DAY, Start, End
FROM(
SELECT A.Day, A.End AS Start, B.Start AS End
FROM cte A
JOIN cte B
ON A.Rownumber = B.Rownumber -1
AND A.Day = B.Day
AND A.End <> B.Start
UNION
SELECT DATE_ADD(A.Day, INTERVAL 1 DAY) AS Day, B.Start AS Start, A.End AS End
FROM cte A
JOIN cte B
ON A.Rownumber = B.Rownumber -1
AND A.Day <> B.Day
)Result
ORDER BY Day ASC, Start ASC
https://www.db-fiddle.com/f/6dm8q8UtmDkkkjExYfMEbx/3
Here is a solution below. Assumed the logic of ID column is identity(1,1), otherwise please generate row_number() for each column first. Syntax might not match with MySql, but you will get the logic and apply same if u like.
CREATE TABLE #result (
[day] date NOT NULL,
[start] time NOT NULL,
[end] time NOT NULL
)
declare #maxid int = (select max(id) from #booking), #counter int = 1, #day date
declare #tempStart time = '00:00', #currentStart time ='00:00'
declare #tempDay date = (select TOP 1 [day] from #booking)
while #counter <= #maxid
begin
set #currentStart = (select start from #booking where id=#counter)
set #day = (select [day] from #booking where id=#counter)
if (#day > DATEADD(day,1,#tempDay))
begin
insert into #result values (DATEADD(day,1,#tempDay),'00:00', '23:00')
set #tempDay = #day
end
if(#tempStart < #currentStart)
begin
insert into #result values (#day, #tempStart, #currentStart)
end
if(#counter = #maxid and #tempStart <> '23:59')
begin
insert into #result values (#day, (select [end] from #booking where id=#counter), '23:59')
end
set #tempStart = (select [end] from #booking where id=#counter)
set #counter = #counter + 1
end
select * from #result
Example for SQL Server: https://rextester.com/live/VBNJ34000

Fill missing values with 0 - sql query

I have an SQL query as outlined below that selects data between two dates.
SELECT date, total FROM db WHERE date >= '2016-03-14' AND date <= '2016-03-20';
I'd like to output a "0" where there's no data for various dates, for example:
Query spans = 2016-03-14 to 2016-03-20
Currently my SQL would output:
Date Total
2016-03-14 50
I'd like to output:
Date Total
2016-03-14 50
2016-03-15 0
2016-03-16 0
2016-03-17 0
2016-03-18 0
2016-03-19 0
2016-03-20 0
Is there any way to do this without complex joins?
Thanks,
Matt
The best way to create records for dates that don't exist in your data is to join to a calendar table.
SELECT a.cal_dt, COALESCE(b.total,0) AS total
FROM lkp_Calendar a
LEFT JOIN db b
ON b.date = a.cal_dt
WHERE a.cal_dt >= '2016-03-14'
AND a.cal_dt <= '2016-03-20';
There are many good scripts out there to create robust calendar tables, a simple one is:
CREATE TABLE lkp_Calendar (cal_dt date);
CREATE PROCEDURE addDates(dateStart DATE, dateEnd DATE)
BEGIN
WHILE dateStart <= dateEnd DO
INSERT INTO lkp_Calendar (cal_dt) VALUES (dateStart);
SET dateStart = date_add(dateStart, INTERVAL 1 DAY);
END WHILE;
END;
CALL addDates('2016-01-01','2016-12-31');
SELECT date, COUNT(*) AS total FROM db WHERE date >= '2016-03-14' AND date <= '2016-03-20' GROUP BY date;
I assume:
date is just a date (has no time part)
total IS NOT a column, just a register count
It doesn't require complex joins. But it does require a rowsource for the missing date values you want returned.
One option is to use a calendar table populated with dates.
create table cal (dt DATE NOT NULL PRIMARY KEY) ... ;
insert into cal (dt) values ('2016-03-01');
insert into cal (dt) select dt + interval 1 day from cal order by dt;
insert into cal (dt) select dt + interval 2 day from cal order by dt;
insert into cal (dt) select dt + interval 4 day from cal order by dt;
insert into cal (dt) select dt + interval 8 day from cal order by dt;
insert into cal (dt) select dt + interval 16 day from cal order by dt;
Then pull the dates from that:
SELECT c.dt
FROM cal c
WHERE c.dt >= '2016-03-14'
AND c.dt < '2016-03-21'
Then just do the simple outer join to your table:
SELECT c.dt AS `date`
, IFNULL(d.total,0) AS `total`
FROM cal c
LEFT
JOIN db d
ON d.date = c.dt
WHERE c.dt >= '2016-03-14'
AND c.dt < '2016-03-21'
ORDER BY c.dt
If you don't have a calendar table, you can use an inline view that does UNION ALL
SELECT c.dt AS `date`
, IFNULL(d.total,0) AS `total`
FROM ( SELECT '2016-03-14' + INTERVAL 0 DAY AS dt
UNION ALL SELECT '2016-03-15' + INTERVAL 0 DAY
UNION ALL SELECT '2016-03-16' + INTERVAL 0 DAY
UNION ALL SELECT '2016-03-17' + INTERVAL 0 DAY
UNION ALL SELECT '2016-03-18' + INTERVAL 0 DAY
UNION ALL SELECT '2016-03-19' + INTERVAL 0 DAY
UNION ALL SELECT '2016-03-20' + INTERVAL 0 DAY
) c
LEFT
JOIN db d
ON d.date = c.dt
ORDER BY c.dt
You can also try the below query. This caters for the scenario where you don't have records for all the dates in the underlying table.
DECLARE #temp TABLE (dbdate DATE, total INT)
DECLARE #StartDate DATE = '2016-03-14'
DECLARE #EndDate DATE = '2016-03-20'
WHILE (#StartDate <= #EndDate)
BEGIN
INSERT #temp
SELECT #StartDate AS [dbDate], ISNULL((SELECT total FROM db WHERE [date] = #StartDate),0) AS Total
SET #StartDate = DATEADD(dd,1,#StartDate)
END
SELECT * FROM #temp
Try this (The fiddle demo is slightly different as I didn't have data of db table)
SQLFiddle Demo
select selected_date,coalesce(count1,0) as count1 from
(select * from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date from
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t0,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t1,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t2,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t3,
(select 0 i union select 1 union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9) t4) v
where selected_date between '2016-03-14' and '2016-03-20'
) t
left join
(SELECT dt, count1 FROM db WHERE dt between '2016-03-14' and '2016-03-20') t1
on t.selected_date=t1.dt

Calculating datetime between two rows, showing one with more than 60 seconds

I have for example a table
id, datetime
1 2014-01-01 01:23:23
2 2014-01-01 01:25:23
3 2014-01-01 01:26:23
4 2014-01-01 01:26:25
7 2014-01-01 01:27:25
8 2014-01-01 01:27:26
I want receive a list of id's that between them there is more than 60 second difference.
My table is very large, when i try to run my query it takes very long. Is there an easier way? Id's in the table are not autoincrement, they can jump in number between them.
SELECT A.id, A.datetime, (B.datetime - A.datetime) AS timedifference
FROM MyTable A CROSS JOIN MyTable B
WHERE B.id IN (SELECT MIN(C.id) FROM MyTable C WHERE C.id > A.id
AND C.datetime between '2014-08-10 00:00:00' AND '2014-08-10 00:01:00')
AND A.datetime between '2014-08-10 00:00:00' AND '2014-08-10 00:01:00'
AND B.datetime between '2014-08-10 00:00:00' AND '2014-08-10 00:01:00'
having timedifference > 60
ORDER BY A.id ASC;
If I understand correctly, you just want to find where there is a gap of one minute or more. I would suggest a correlated subquery:
select a.id, a.datetime,
(select id
from mytable b
where b.datetime > a.datetime + interval 1 minute
order by b.datetime
limit 1
) as nextid
from mytable a
where A.datetime between '2014-08-10 00:00:00' AND '2014-08-10 00:01:00'
having nextid is not null;
For performance, you want an index on mytable(datetime, id).
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,datetime DATETIME NOT NULL
);
INSERT INTO my_table VALUES
(1,'2014-01-01 01:23:23'),
(2,'2014-01-01 01:25:23'),
(3,'2014-01-01 01:26:23'),
(4,'2014-01-01 01:26:25'),
(7,'2014-01-01 01:27:25'),
(8,'2014-01-01 01:27:26');
SELECT a.id
, TIMEDIFF(b.datetime,a.datetime)
FROM (SELECT x.*, #i:=#i+1 rank FROM my_table x, (SELECT #i:=0) i ORDER BY x.id) a
JOIN (SELECT x.*, #j:=#j+1 rank FROM my_table x, (SELECT #j:=0) j ORDER BY x.id) b
ON b.rank = a.rank + 1
AND b.datetime > a.datetime + INTERVAL 60 SECOND;
+----+---------------------------------+
| id | TIMEDIFF(b.datetime,a.datetime) |
+----+---------------------------------+
| 1 | 00:02:00 |
+----+---------------------------------+