Time difference between rows - mysql

In a mysql database (ver.5.7.15) I've got the following table named operation_list containing the following data: id (autoincrement integer, primary key), operation_date_time (datetime), operation (enumerated values: START,STOP), so the table looks like that:
+------+---------------------+-----------+
| id | operation_date_time | operation |
+------+---------------------+-----------+
| 1 | 2000-01-01 06:30:45 | START |
| 2 | 2000-01-01 07:45:00 | STOP |
| 3 | 2000-01-01 08:18:12 | START |
| 4 | 2000-01-01 11:23:58 | STOP |
| 5 | 2000-01-01 15:45:01 | START |
| 6 | 2000-01-01 19:01:33 | STOP |
+------+---------------------+-----------+
Now, assuming that the first row is always a START, the last ROW is always a STOP, the STOP is always placed after a START, I need to retrieve the time difference between START and STOP in seconds. Hence, I need to write an SQL that would produce the following recordset:
+------+---------------------+-----------+
| id | operation_date_time | duration |
+------+---------------------+-----------+
| 1 | 2000-01-01 06:30:45 | 4455 |
| 3 | 2000-01-01 08:78:12 | 11146 |
| 5 | 2000-01-01 15:45:01 | 11792 |
+------+---------------------+-----------+
Where 4455 is equivalent to 1 hour, 14 minutes and 15 seconds,
11146 is equivalent to 3 hours, 5 minutes and 46 seconds,
11792 is equivalent to 3 hours, 16 minutes and 32 seconds, and so on.
What's the best way to do it in a single SQL statement without creating additional tables or dedicated scripting?

This works IN mysql 5.X
But it is uglier as in 8.0
SELECT
MIN(id) id
,MIN(`operation_date_time`) `operation_date_time`
,MAX(diff) duration
FROM
(SELECT
id
, IF(`operation` = 'START', 0,TIME_TO_SEC(TIMEDIFF(`operation_date_time`, #datetime))) diff
,IF(`operation` = 'START', #count := #count + 1,#count := #count) groupby
,#datetime := `operation_date_time` `operation_date_time`
FROM
(SELECT * FROM timetable ORDER by `operation_date_time` ASC) t1, (SELECT #datetime := NOW()) a,
(SELECT #count := 0) b) t2
GROUP by groupby;
CREATE TABLE timetable (
`id` INTEGER,
`operation_date_time` VARCHAR(19),
`operation` VARCHAR(5)
);
INSERT INTO timetable
(`id`, `operation_date_time`, `operation`)
VALUES
('1', '2000-01-01 06:30:45', 'START'),
('2', '2000-01-01 07:45:00', 'STOP'),
('3', '2000-01-01 08:18:12', 'START'),
('4', '2000-01-01 11:23:58', 'STOP'),
('5', '2000-01-01 15:45:01', 'START'),
('6', '2000-01-01 19:01:33', 'STOP');
✓
✓
SELECT
MIN(id) id
,MIN(`operation_date_time`) `operation_date_time`
,MAX(diff) duration
FROM
(SELECT
id
, IF(`operation` = 'START', 0,TIME_TO_SEC(TIMEDIFF(`operation_date_time`, #datetime))) diff
,IF(`operation` = 'START', #count := #count + 1,#count := #count) groupby
,#datetime := `operation_date_time` `operation_date_time`
FROM
(SELECT * FROM timetable ORDER by `operation_date_time` ASC) t1, (SELECT #datetime := NOW()) a,
(SELECT #count := 0) b) t2
GROUP by groupby;
id | operation_date_time | duration
-: | :------------------ | -------:
1 | 2000-01-01 06:30:45 | 4455
3 | 2000-01-01 08:18:12 | 11146
5 | 2000-01-01 15:45:01 | 11792
db<>fiddle here

SELECT operation_date_time,DURATION FROM (
SELECT *,DATEDIFF(SECOND,operation_date_time,LEAD(operation_date_time)OVER(ORDER BY ID)) AS DURATION FROM PRACTICE
)A
WHERE operation='START'

Use window functions to get the ending time. I am going to use a cumulative conditional min:
select t.*,
timestampdiff(second, operation_date_time, stop_dt) as diff_seconds
from (select t.*,
min(case when operation = 'STOP' then operation_date_time end) over (order by operation_date_time) as stop_dt
from t
) t
where operation = 'START';
If the data really is interleaved, then you could just use lead():
select t.*,
timestampdiff(second, operation_date_time, stop_dt) as diff_seconds
from (select t.*,
lead(operation_date_time) over (order by operation_date_time) as stop_dt
from t
) t
where operation = 'START';
EDIT:
In MySQL pre-8.0:
select t.*,
timestampdiff(second, operation_date_time, stop_dt) as diff_seconds
from (select t.*,
(select min(t2.operation_date_time)
from t t2
where t2.operation = 'STOP' and
t2.operation_date_time > t.operation_date_time
) as stop_dt
from t
) t
where operation = 'START';

Related

How to "Fill Series" (Linear step value) in MySQL like in Excel?

In Excel, filling series with linear step value is simple. How do I do that in MySQL?
(1) SELECT * FROM blog_posts where postid = 5 ORDER BY rowid ASC
I get this query result from a huge table:
rowid postid Unix_TimeStamp
100 5 1000000000
135 5 1656885375
142 5 1885649882
208 5 1928211766
(2)Next, I I need to alter the values of Unix_TimeStamp. I want to leave the first row (rowid=100) alone, then every row's Unix_TimeStamp is 100 higher than the previous row's. The result would be:
rowid postid Unix_TimeStamp
100 5 1000000000
135 5 1000000100
142 5 1000000200
208 5 1000000300
Thanks a lot for generous replies.
In mysql 5.x you can do this like
In mysql 8 you have the window function rownumber
Schema (MySQL v5.7)
CREATE TABLE blog_posts (
`rowid` INTEGER,
`postid` INTEGER,
`Unix_TimeStamp` INTEGER
);
INSERT INTO blog_posts
(`rowid`, `postid`, `Unix_TimeStamp`)
VALUES
('100', '5', '1000000000'),
('135', '5', '1656885375'),
('142', '5', '1885649882'),
('208', '5', '1928211766');
Query #1
SELECT
`rowid`, `postid`
,(SELECT MIN(`Unix_TimeStamp`) FROM blog_posts where postid = 5 ) + #rn *100 `Unix_TimeStamp`
,#rn := #rn + 1 ronn
FROM blog_posts, (SELECT #rn := 0) a
where postid = 5
ORDER BY rowid ASC;
| rowid | postid | Unix_TimeStamp | ronn |
| ----- | ------ | -------------- | ---- |
| 100 | 5 | 1000000000 | 1 |
| 135 | 5 | 1000000100 | 2 |
| 142 | 5 | 1000000200 | 3 |
| 208 | 5 | 1000000300 | 4 |
UPDATE blog_posts bp INNER JOIN (SELECT
`rowid`, `postid`
,(SELECT MIN(`Unix_TimeStamp`) FROM blog_posts where postid = 5 ) + #rn *100 `Unix_TimeStamp`
,#rn := #rn + 1 ronn
FROM blog_posts, (SELECT #rn := 0) a
where postid = 5
ORDER BY rowid ASC) t1 ON bp.rowid = t1.rowid
SET bp.Unix_TimeStamp = t1.Unix_TimeStamp;
[View on DB Fiddle](https://www.db-fiddle.com/f/wUqVKNZy96RjR7hTk3md7o/4)

Subtract Next to the current and make that output as the data of that current row

Computing Kilometer Run is based on this
current value = next value - current value
I have a table that looks like this.
My question is how can I compute the kmr based on the odometer value? I will replace the value of kmr column of kmr value
You can use variables to store last values.
create table tbl (code varchar(10), vdate date, kmr int);
✓
insert into tbl values
('Person1', '20180101', 71883),
('Person1', '20180102', 71893),
('Person1', '20180103', 71903),
('Person2', '20180101', 71800),
('Person2', '20180102', 71815),
('Person2', '20180103', 71820);
✓
select code, vdate, kmr, current_kmr
from
(
select t1.code, t1.vdate, t1.kmr,
t1.kmr - if(coalesce(#last_code, t1.code) = t1.code, coalesce(#last_kmr, t1.kmr), t1.kmr) as current_kmr,
#last_kmr := t1.kmr,
#last_code := t1.code
from tbl t1,
(select #last_kmr := null, #last_code := null) t2
order by t1.code, t1.vdate
) t
code | vdate | kmr | current_kmr
:------ | :--------- | ----: | ----------:
Person1 | 2018-01-01 | 71883 | 0
Person1 | 2018-01-02 | 71893 | 10
Person1 | 2018-01-03 | 71903 | 10
Person2 | 2018-01-01 | 71800 | 0
Person2 | 2018-01-02 | 71815 | 15
Person2 | 2018-01-03 | 71820 | 5
dbfiddle here
This will work using rank and for MORE THAN one person
http://sqlfiddle.com/#!9/4c054e/1
Select m.`code`,m.vdate, ( n.kmr - m.kmr) as new_kmr
From
(Select t1.*, #rnk := #rnk + 1 as rnk
From tbl t1, (select #rnk := 0) t
Order by t1.`code`,t1.vdate) m left join
(Select t2.*, #rnk1 := #rnk1 + 1 as rnk
From tbl t2, (select #rnk1:= 0) t
Order by t2.`code`,t2.vdate) n
On m.`code` = n.`code`
And m.rnk + 1 = n.rnk
Order by m.`code`, m.vdate
Output:
code vdate new_kmr
person 1 2018-03-01 10
person 1 2018-03-02 10
person 1 2018-03-03 (null)
person 2 2018-03-01 5
person 2 2018-03-02 (null)

how to calculate maximum granularity (or intervals) between dates in table in mysql

I have table with 3 columns like
NAME DATE_TIME AMOUNT
T1 2012:10:01 12:15:00 200
T1 2012:10:01 12:30:00 300
T1 2012:10:02 01:00:00 400
T1 2012:10:02 01:15:00 500
T1 2012:10:03 04:00:00 600
T1 2012:10:03 05:00:00 700
T2 2013:12:31 22:30:00 3838
T2 2013:12:31 23:00:00 7272
T2 2013:12:31 23:30:00 891
T2 2013:12:31 23:45:00 288
In the table T1 value the maximum interval is 15 mins and for T2 the maximun intervals is 30 Mins ,the output would be
NAME Granularity/max.interval
T1 15 (mins)
T2 30 (mins)
Thanks in advance
Here is a solution with a full demo.
-- SQL needed
SELECT
NAME, Granularity, cnt
FROM
(
SELECT
NAME, Granularity, COUNT(*) cnt
FROM
(
SELECT
*,
IF(#last_name IS NULL OR NAME != #last_name OR DATE(#last_dt) != DATE(DATE_TIME),
0, TIMESTAMPDIFF(MINUTE, #last_dt, DATE_TIME))AS Granularity,
#last_name:=NAME,
#last_dt:=DATE_TIME
FROM t1 JOIN (SELECT #last_dt:=NULL, #last_name:=NULL) v
) t
WHERE t.Granularity > 0
GROUP BY NAME, Granularity
) tt
WHERE
(NAME, cnt) IN
(
SELECT NAME, MAX(cnt) max_cnt
FROM
(
SELECT
NAME, Granularity, COUNT(*) cnt
FROM
(
SELECT
*,
IF(#last_name IS NULL OR NAME != #last_name OR DATE(#last_dt) != DATE(DATE_TIME),
0, TIMESTAMPDIFF(MINUTE, #last_dt, DATE_TIME))AS Granularity,
#last_name:=NAME,
#last_dt:=DATE_TIME
FROM t1 JOIN (SELECT #last_dt:=NULL, #last_name:=NULL) v
) t
WHERE t.Granularity > 0
GROUP BY NAME, Granularity
) ttt
GROUP BY NAME
);
The following is a full demo using the data you provided.
SQL:
-- data
create table t1(NAME char(20), DATE_TIME datetime, AMOUNT int);
insert into t1 values
('T1', '2012:10:01 12:15:00', 200),
('T1', '2012:10:01 12:30:00', 300),
('T1', '2012:10:02 01:00:00', 400),
('T1', '2012:10:02 01:15:00', 500),
('T1', '2012:10:03 04:00:00', 600),
('T1', '2012:10:03 05:00:00', 700),
('T2', '2013:12:31 22:30:00', 3838),
('T2', '2013:12:31 23:00:00', 7272),
('T2', '2013:12:31 23:30:00', 891),
('T2', '2013:12:31 23:45:00', 288);
SELECT * FROM t1;
-- SQL needed
SELECT
NAME, Granularity, cnt
FROM
(
SELECT
NAME, Granularity, COUNT(*) cnt
FROM
(
SELECT
*,
IF(#last_name IS NULL OR NAME != #last_name OR DATE(#last_dt) != DATE(DATE_TIME),
0, TIMESTAMPDIFF(MINUTE, #last_dt, DATE_TIME))AS Granularity,
#last_name:=NAME,
#last_dt:=DATE_TIME
FROM t1 JOIN (SELECT #last_dt:=NULL, #last_name:=NULL) v
) t
WHERE t.Granularity > 0
GROUP BY NAME, Granularity
) tt
WHERE
(NAME, cnt) IN
(
SELECT NAME, MAX(cnt) max_cnt
FROM
(
SELECT
NAME, Granularity, COUNT(*) cnt
FROM
(
SELECT
*,
IF(#last_name IS NULL OR NAME != #last_name OR DATE(#last_dt) != DATE(DATE_TIME),
0, TIMESTAMPDIFF(MINUTE, #last_dt, DATE_TIME))AS Granularity,
#last_name:=NAME,
#last_dt:=DATE_TIME
FROM t1 JOIN (SELECT #last_dt:=NULL, #last_name:=NULL) v
) t
WHERE t.Granularity > 0
GROUP BY NAME, Granularity
) ttt
GROUP BY NAME
);
Output:
mysql> SELECT * FROM t1;
+------+---------------------+--------+
| NAME | DATE_TIME | AMOUNT |
+------+---------------------+--------+
| T1 | 2012-10-01 12:15:00 | 200 |
| T1 | 2012-10-01 12:30:00 | 300 |
| T1 | 2012-10-02 01:00:00 | 400 |
| T1 | 2012-10-02 01:15:00 | 500 |
| T1 | 2012-10-03 04:00:00 | 600 |
| T1 | 2012-10-03 05:00:00 | 700 |
| T2 | 2013-12-31 22:30:00 | 3838 |
| T2 | 2013-12-31 23:00:00 | 7272 |
| T2 | 2013-12-31 23:30:00 | 891 |
| T2 | 2013-12-31 23:45:00 | 288 |
+------+---------------------+--------+
10 rows in set (0.00 sec)
...
+------+-------------+-----+
| NAME | Granularity | cnt |
+------+-------------+-----+
| T1 | 15 | 2 |
| T2 | 30 | 2 |
+------+-------------+-----+
2 rows in set (0.00 sec)

mysql: need null fields when join doesn't match

I´m strugling with these two tables on MySQL.
select emp_id, contrato_id,date from organização_rh;
+--------+-------------+------------+
| emp_id | contrato_id | date |
+--------+-------------+------------+
| 1 | 1 | 2000-01-01 |
| 1 | 2 | 2000-01-10 |
| 1 | 3 | 2000-02-01 |
| 2 | 1 | 1999-01-01 |
+--------+-------------+------------+
select id, codigo from contratotipo;
+----+---------------+
| id | codigo |
+----+---------------+
| 2 | determinado |
| 3 | fim |
| 1 | indeterminado |
+----+---------------+
What I’m trying to do is to join them in a way that where an employee didn’t sign a contract the date field is set to NULL. That is, I’d like to have an output as the following:
+--------+-------------+------------+
| emp_id | contrato_id | date |
+--------+-------------+------------+
| 1 | 1 | 2000-01-01 |
| 1 | 2 | 2000-01-10 |
| 1 | 3 | 2000-02-01 |
| 2 | 1 | 1999-01-01 |
| 2 | 2 | NULL |
| 2 | 3 | NULL |
+--------+-------------+------------+
I’ve tried different joins to no avail, and, so far, none of them shows me a row with a NULL value in the date field. So, for example if I run
SELECT emp_id,contrato_id, date
FROM organização_rh as o right outer JOIN contratotipo c
ON o.contrato_id = c.id;
I don't get any NULL values when rows don’t match.
+--------+-------------+------------+
| emp_id | contrato_id | date |
+--------+-------------+------------+
| 1 | 3 | 2000-02-01 |
| 1 | 2 | 2000-01-10 |
| 1 | 1 | 2000-01-01 |
| 2 | 1 | 1999-01-01 |
+--------+-------------+------------+
Any help would be much appreciated!!
#John Ruddell thanks for your help.
I came up with this solution that is not that elegant as it uses cursors, but it works with any number of employees and contracts. Your solution seems more alegant as everything fits in a just one query. However, I wasn't able to adapted it to a more general case.
BEGIN
declare var_contrato_id int unsigned;
declare contrato_finished int default 0;
DECLARE contrato_cursor CURSOR FOR SELECT id FROM contratotipo;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET contrato_finished = 1;
OPEN contrato_cursor;
drop table if exists temp_sp_rh_turnover;
CREATE TEMPORARY TABLE temp_sp_rh_turnover AS (select distinct org_id, emp_id, cast( 0 AS unsigned) as contrato_id, cast('1000-1-1' as date) as data from organização_rh where emp_id=-1);
conts: LOOP
FETCH contrato_cursor INTO var_contrato_id;
IF contrato_finished = 1 THEN
LEAVE conts;
END IF;
insert into temp_sp_rh_turnover
select distinct org_id, emp_id, var_contrato_id, NULL from organização_rh;
end loop;
select
t.org_id, t.emp_id, t.contrato_id, o.data
from
temp_sp_rh_turnover as t
left join
organização_rh as o
on
t.org_id = o.org_id and
t.emp_id = o.emp_id and
t.contrato_id = o.contrato_id;
drop table if exists temp_sp_rh_turnover;
END
To do something like this is not easy in mysql and itll require a lot of manual work. from what I understand you want to show a new row per row in your other table..
meaning
for every emp_id in table o, you want there to be 3 rows related to it, one relating to each of the three rows in table c.
now to do something like that is not simple at all since there is no way to manually add rows, unless you are doing an insert or a bunch of unions. so with this dataset (which im assuming is small compared to what you are working with) you can get those desired results like this.
SET #row_num := 1;
INSERT INTO organização_rh (emp_id, contrato_id, date)
SELECT
emp_id, #ROW_NUM := #ROW_NUM + 1, NULL
FROM organização_rh
WHERE emp_id IN
( SELECT emp_id
FROM organização_rh
GROUP BY emp_id
HAVING COUNT(*) < 3
)
AND #ROW_NUM < 3;
run this insert query twice and i'll give you the output you want.
OUTPUT:
23:20:25 set #row_num := 1 0 row(s) affected 0.000 sec
23:20:30 INSERT INTO organização_rh (emp_id, contrato_id, date) SELECT emp_id, #ROW_NUM := #ROW_NUM + 1, NULL FROM organização_rh WHERE emp_id IN ( SELECT emp_id FROM organização_rh GROUP BY emp_id HAVING COUNT(*) < 3 ) AND #ROW_NUM < 3 1 row(s) affected Records: 1 Duplicates: 0 Warnings: 0 0.001 sec
23:20:31 INSERT INTO organização_rh (emp_id, contrato_id, date) SELECT emp_id, #ROW_NUM := #ROW_NUM + 1, NULL FROM organização_rh WHERE emp_id IN ( SELECT emp_id FROM organização_rh GROUP BY emp_id HAVING COUNT(*) < 3 ) AND #ROW_NUM < 3 1 row(s) affected Records: 1 Duplicates: 0 Warnings: 0 0.001 sec
23:20:32 INSERT INTO organização_rh (emp_id, contrato_id, date) SELECT emp_id, #ROW_NUM := #ROW_NUM + 1, NULL FROM organização_rh WHERE emp_id IN ( SELECT emp_id FROM organização_rh GROUP BY emp_id HAVING COUNT(*) < 3 ) AND #ROW_NUM < 3 0 row(s) affected Records: 0 Duplicates: 0 Warnings: 0 0.002 sec
if you notice the last time i tried to do it it wouldn't do it because the row num was max'd out. this will be some manual labor but its basically the only way to do it solely in mysql.
after inserting this is the result set.
+-------+---------------+---------------+
|emp_id | contrato_id, | date |
+-------+---------------+---------------+
|'1', | '1', | '2000-01-01'|
|'1', | '2', | '2000-01-10'|
|'1', | '3', | '2000-02-01'|
|'2', | '1', | '1999-01-01'|
|'2', | '2', | NULL |
|'2', | '3', | NULL |
+-------+---------------+---------------+
NOTE: this is for updating the table. if you wanted just a query with this output, you will need to union a new row for each one which isn't really recommended. to be honest you need to fix your table structure. add a column that you could do a cartesian product join on or something.
anyways here is the union query.
SELECT emp_id, contrato_id, date FROM organização_rh
CROSS JOIN(SELECT #ROW_NUM := 0)t
UNION ALL
SELECT
emp_id, #ROW_NUM := #ROW_NUM + 1, NULL
FROM
organização_rh
WHERE emp_id IN
( SELECT emp_id
FROM organização_rh
GROUP BY emp_id
HAVING COUNT(*) < 3
)
AND #ROW_NUM < 3
UNION ALL
SELECT
emp_id, #ROW_NUM := #ROW_NUM + 1, NULL
FROM
organização_rh
WHERE emp_id IN
( SELECT emp_id
FROM organização_rh
GROUP BY emp_id
HAVING COUNT(*) < 3
)
AND #ROW_NUM < 3;
OUTPUT is again the same as the insert
+-------+---------------+---------------+
|emp_id | contrato_id, | date |
+-------+---------------+---------------+
|'1', | '1', | '2000-01-01'|
|'1', | '2', | '2000-01-10'|
|'1', | '3', | '2000-02-01'|
|'2', | '1', | '1999-01-01'|
|'2', | '2', | NULL |
|'2', | '3', | NULL |
+-------+---------------+---------------+
The reason you are having trouble is that you cannot accomplish this without a third table listing all employees.
You might argue that you could get this information with
select distinct emp_id from organização_rh
but this would not cover the case where there is an employee that has not signed ANY contracts. Certainly you would want that employee to appear in the output with null dates for all contracts.
Create an employee table and then the join should be obvious.
Outer join the table to itself. Something like:
select emp_id, contrato_id,date from organização_rh as a
JOIN
SELECT DISTINCT contrato_id FROM organização_rh as b
WHERE a.contrato_id = b.contrato_id
should work.
Alternatively you could
select emp_id, contrato_id,date from organização_rh as a
JOIN
select DISTINCT id from contratotipo as b
WHERE a.contrato_id = b.id
If thats more to your liking.
#John Ruddell
This works great with the original values.
I added a new employee: emp_id=3, contrato_id=1, date=2001-1-1. If I run the query, I get that contract 3 (for emp_id=3)is missing.
|emp_id|contrato_id| date |
+------+-----------+------------+
|'1', | '1', |'2000-01-01'|
|'1', | '2', |'2000-01-10'|
|'1', | '3', |'2000-02-01'|
|'2', | '1', |'1999-01-01'|
|'2', | '2', | NULL |
|'2', | '3', | NULL |
|'3', | '1', |'2001-01-01'|
|'3', | '2', | NULL |
Do I have to run the query:
SELECT
emp_id, #ROW_NUM := #ROW_NUM + 1, NULL
FROM
organização_rh
WHERE emp_id IN
( SELECT emp_id
FROM organização_rh
GROUP BY emp_id
HAVING COUNT(*) < 3
)
AND #ROW_NUM < 3
for each user after resetting #ROW_NUM?

Calculate delta(difference of current and previous row) mysql group by specific column

I have a table like : session is the name of the table for example
With columns: Id, sessionDate, user_id
What i need:
Delta should be a new calculated column
Id | sessionDate | user_id | Delta in days
------------------------------------------------------
1 | 2011-02-20 00:00:00 | 2 | NULL
2 | 2011-03-21 00:00:00 | 2 | NULL
3 | 2011-04-22 00:00:00 | 2 | NULL
4 | 2011-02-20 00:00:00 | 4 | NULL
5 | 2011-03-21 00:00:00 | 4 | NULL
6 | 2011-04-22 00:00:00 | 4 | NULL
Delta is the Difference between the timestamps
What i want is a result for Delta Timestamp (in Days) for the the previous row and the current row grouped by the user_id.
this should be the result:
Id | sessionDate | user_id | Delta in Days
------------------------------------------------------
1 | 2011-02-20 00:00:00 | 2 | NULL
2 | 2011-02-21 00:00:00 | 2 | 1
3 | 2011-02-22 00:00:00 | 2 | 1
4 | 2011-02-20 00:00:00 | 4 | NULL
5 | 2011-02-23 00:00:00 | 4 | 3
6 | 2011-02-25 00:00:00 | 4 | 2
I already have a solution for a specific user_id:
SELECT user_id, sessionDate,
abs(DATEDIFF((SELECT MAX(sessionDate) FROM session WHERE sessionDate < t.sessionDate and user_id = 1), sessionDate)) as Delta_in_days
FROM session AS t
WHERE t.user_id = 1 order by sessionDate asc
But for more user_ids i didn´t find any solution
Hope somebody can help me.
Try this:
drop table a;
create table a( id integer not null primary key, d datetime, user_id integer );
insert into a values (1,now() + interval 0 day, 1 );
insert into a values (2,now() + interval 1 day, 1 );
insert into a values (3,now() + interval 2 day, 1 );
insert into a values (4,now() + interval 0 day, 2 );
insert into a values (5,now() + interval 1 day, 2 );
insert into a values (6,now() + interval 2 day, 2 );
select t1.user_id, t1.d, t2.d, datediff(t2.d,t1.d)
from a t1, a t2
where t1.user_id=t2.user_id
and t2.d = (select min(d) from a t3 where t1.user_id=t3.user_id and t3.d > t1.d)
Which means: join your table to itself on user_ids and adjacent datetime entries and compute the difference.
If id is really sequential (as in your sample data), the following should be quite efficient:
select t.id, t.sessionDate, t.user_id, datediff(t2.sessiondate, t.sessiondate)
from table t left outer join
table tprev
on t.user_id = tprev.user_id and
t.id = tprev.id + 1;
There is also another efficient method using variables. Something like this should work:
select t.id, t.sessionDate, t.user_id, datediff(prevsessiondate, sessiondate)
from (select t.*,
if(#user_id = user_id, #prev, NULL) as prevsessiondate,
#prev := sessiondate,
#user_id := user_id
from table t cross join
(select #user_id := 0, #prev := 0) vars
order by user_id, id
) t;
(There is a small issue with these queries where the variables in the select clause may not be evaluated in the order we expect them to. This is possible to fix, but it complicates the query and this will usually work.)
Although you have choosen an answer here is another way of achieving it
SELECT
t1.Id,
t1.sessionDate,
t1.user_id,
TIMESTAMPDIFF(DAY,t2.sessionDate,t1.sessionDate) as delta
from myTable t1
left join myTable t2
on t1.user_id = t2.user_id
AND t2.Id = (
select max(Id) from myTable t3
where t1.Id > t3.Id AND t1.user_id = t3.user_id
);
DEMO