MySQL SELECT output next date per id in same row - mysql

I have the following table structure that I query on.
ID Date Before value After value
1 2014-04-25 Win Loss
1 2014-04-30 Loss Win
1 2014-08-18 Win Loss
1 2014-08-27 Loss Remise
1 2014-09-05 Remise Loss
2 2014-05-25 Win Remise
2 2014-06-07 Remise Win
2 2014-06-20 Win Loss
As output of my select statement I want the following result:
ID Start_Date End_Date After value
1 start 2014-04-25 Win
1 2014-04-26 2014-04-30 Loss
1 2014-05-01 2014-08-18 Win
1 2014-08-19 2014-08-27 Loss
1 2014-08-28 2014-09-05 Remise
1 2014-09-06 current Loss
2 start 2014-05-25 Win
2 2014-05-26 2014-06-07 Remise
2 2014-06-08 2014-06-20 Win
2 2014-06-21 current Loss
Of course the table has 1000's of records where it needs to go right. I tried with ranking and joining, but no luck yet. If there is a next row I somehow need to retrieve this value as start date +1.
http://sqlfiddle.com/#!2/520b13/2

This approach uses MySql inline variables to preserve the last ID and last date for comparison to the next record.
SELECT
t.id,
IF( #LastID = t.id, #LastDate, 'start ' ) as StartDate,
IF( NOT t.date = t2.date, t.date, 'current ' ) as EndDate,
t.after_value,
#LastID := t.id as saveForNextLineID,
#LastDate := t.date + INTERVAL 1 day as saveForNextDate
from
the_table t
left join ( select id, max( date ) as date
from the_table
group by id ) t2
ON t.id = t2.id,
(select #LastID := 0, #LastDate := 'start ' ) sqlvars
order by
t.id,
t.date
SQL Fiddle sample

Reading between the lines somewhat but you might be able to use UNION to manually add a start and end date eg:
select * from mytable
union
select DISTINCT ID,'2000-01-01' as `Date`,`Before value`,``After value` from mytable
union
select DISTINCT ID,NOW() as `Date`,`Before value`,``After value` from mytable
Try Popping this in a sub-select and run your grouping code on that.

Something like:
select t1.id,
(select max(date) + interval '1' day
from the_table t2
where t2.date < t1.date
and t2.id = t1.id) as start_date,
t1.date as end_date,
t1.after_value
from the_table t1
order by t1.id, t1.date;
SQLFiddle: http://sqlfiddle.com/#!2/8905a/3

Related

Mysql join row in table to next row by id (not sequential) [duplicate]

I have the following table structure that I query on.
ID Date Before value After value
1 2014-04-25 Win Loss
1 2014-04-30 Loss Win
1 2014-08-18 Win Loss
1 2014-08-27 Loss Remise
1 2014-09-05 Remise Loss
2 2014-05-25 Win Remise
2 2014-06-07 Remise Win
2 2014-06-20 Win Loss
As output of my select statement I want the following result:
ID Start_Date End_Date After value
1 start 2014-04-25 Win
1 2014-04-26 2014-04-30 Loss
1 2014-05-01 2014-08-18 Win
1 2014-08-19 2014-08-27 Loss
1 2014-08-28 2014-09-05 Remise
1 2014-09-06 current Loss
2 start 2014-05-25 Win
2 2014-05-26 2014-06-07 Remise
2 2014-06-08 2014-06-20 Win
2 2014-06-21 current Loss
Of course the table has 1000's of records where it needs to go right. I tried with ranking and joining, but no luck yet. If there is a next row I somehow need to retrieve this value as start date +1.
http://sqlfiddle.com/#!2/520b13/2
This approach uses MySql inline variables to preserve the last ID and last date for comparison to the next record.
SELECT
t.id,
IF( #LastID = t.id, #LastDate, 'start ' ) as StartDate,
IF( NOT t.date = t2.date, t.date, 'current ' ) as EndDate,
t.after_value,
#LastID := t.id as saveForNextLineID,
#LastDate := t.date + INTERVAL 1 day as saveForNextDate
from
the_table t
left join ( select id, max( date ) as date
from the_table
group by id ) t2
ON t.id = t2.id,
(select #LastID := 0, #LastDate := 'start ' ) sqlvars
order by
t.id,
t.date
SQL Fiddle sample
Reading between the lines somewhat but you might be able to use UNION to manually add a start and end date eg:
select * from mytable
union
select DISTINCT ID,'2000-01-01' as `Date`,`Before value`,``After value` from mytable
union
select DISTINCT ID,NOW() as `Date`,`Before value`,``After value` from mytable
Try Popping this in a sub-select and run your grouping code on that.
Something like:
select t1.id,
(select max(date) + interval '1' day
from the_table t2
where t2.date < t1.date
and t2.id = t1.id) as start_date,
t1.date as end_date,
t1.after_value
from the_table t1
order by t1.id, t1.date;
SQLFiddle: http://sqlfiddle.com/#!2/8905a/3

mysql 5.7.12 - how to sequentially get the difference of two columns?

I have a table with process ids, start times, and end times.
pid start end
1 2020-01-01 01:00:00 2020-01-01 02:00:00
1 2020-01-02 01:00:00 2020-01-02 01:30:00
1 2020-01-03 01:00:00 2020-01-03 01:10:00
I need to do some analysis on this so I need the last 5 run times in the format
pid t1 t2 t3 t4 t5
1 10 30 60
I can get the latest run time by subtracting max value of start and end time stamps but how do I get the rest?
One option uses window functions (available in MySQL 8.0) and date arithmetics:
select pid,
max(case when rn = 1 then timestampdiff(minute, startts, endts) end) t1,
max(case when rn = 2 then timestampdiff(minute, startts, endts) end) t2,
max(case when rn = 3 then timestampdiff(minute, startts, endts) end) t3,
max(case when rn = 4 then timestampdiff(minute, startts, endts) end) t4,
max(case when rn = 5 then timestampdiff(minute, startts, endts) end) t5
from (
select t.*, row_number() over(partition by pid order startts desc, endts desc) rn
from mytable t
) t
where rn <= 5
group by pid
Notes:
start and end are language keywords; I renamed the columns to startts and endts in the query
this defines the last 5 runs as the runs with the greatest startts and endts
this puts the latest run in the first column, which is slightly different than your expected result, but makes more sense to me

Get start of process and end of process in a datetime range

MySQL query to find start and end of process in a given date range. There can be multiple processes in the given date range. The query I've written gives the start and end correctly if there is only one process. If there are 2 processes, it gives the start date-time stamp of the 1st process and the end date-time stamp of the second process. How can I get the start time and end time stamp of both processes (the 1st process and 2nd process)?
Table contains two columns
date_time, datetime
value, bit
Table 1 sample data
date_time value
2020-02-19 00:10:00 0
2020-02-19 00:11:00 0
2020-02-19 00:12:00 1
2020-02-19 00:13:00 1
2020-02-19 00:14:00 1
2020-02-19 00:15:00 0
2020-02-19 00:16:00 0
2020-02-19 00:17:00 1
2020-02-19 00:18:00 1
2020-02-19 00:19:00 1
2020-02-19 00:20:00 0
Sample output
Rowno date_time value
3. 2020-02-19 00:12:00 1
6. 2020-02-19 00:15:00 0
8. 2020-02-19 00:17:00 1
11. 2020-02-19 00:20:00 0
Note: When process begins value is 1 else it is 0. To identiify start time we have to get the first row with value 1 and previous value 0. Similarly for end time we have to identify last row with value 1 and next row value 0.
Query:
-- Identify 1st row with value 1
SET #row_number = 0, #result = 0;
select #result := (a.num - 1) as prev_rec, a.num, a.date_time, a.value from (
SELECT (#row_number:=#row_number + 1) AS num, date_time, value
FROM table1
where date_time >= '2020-02-19 00:00:00' and date_time <= '2020-02-25 23:59:00') as a
where a.value = 1
order by a.date_time limit 1;
-- Check if value for previous rec is 0 to identify start time
SET #row_number = 0;
select a.num, a.date_time, a.value from (
SELECT (#row_number:=#row_number + 1) AS num, date_time, value
FROM table1
where date_time >= '2020-02-19 00:00:00' and date_time <= '2020-02-25 23:59:00') as a
where a.num = #result
order by a.date_time limit 1;
Similarly I look for end time stamp
-- Identify last row with value 1
SET #row_number = 0, #result = 0;
select #result := (a.num + 1) as next_rec, a.num, a.date_time, a.value from (
SELECT (#row_number:=#row_number + 1) AS num, date_time, value
FROM table1
where date_time >= '2020-02-19 00:00:00' and date_time <= '2020-02-23 23:59:59') as a
where a.value = 1
order by a.date_time desc limit 1;
-- Check if value for next rec is 0 to identify end time
SET #row_number = 0;
select a.num, a.date_time, a.value from (
SELECT (#row_number:=#row_number + 1) AS num, date_time, value
FROM table1
where date_time >= '2020-02-19 00:00:00' and date_time <= '2020-02-23 23:59:59') as a
where a.num = #result
order by a.date_time limit 1;
Can I get a way to get a solution?
Please forgive me if I have left out anything relevant. I'm asking a question for the first time here.
To do this in mysql 8 or mariadb 10.2 or above you simply use the lag window function:
select date_time, value from (
select date_time, value, lag(value) over (order by date_time) previous_value
from mysterytable
) mysterytable_with_lag
where value != previous_value
order by date_time
(optionally add or value and previous_value is null to the where clause if you want to include the earliest row when its value is 1).
For earlier versions, you can emulate the lag function with variables:
select date_time, value from (
select date_time, value, previous_value
from (select #previous_value := null) initvars
cross join (
select date_time, #previous_value previous_value, #previous_value := value value
from mysterytable
order by date_time
) mysterytable_post_initvars
) mysterytable_with_lag
where value != previous_value
order by date_time

how to calculate days difference of vaules in same column?

I have data like this:
select * from date_table;
startdate
2018-08-22
2018-08-24
2018-08-27
2018-08-29
2018-08-31
2018-09-05
2018-09-07
2018-09-10
I have written this query, which only gives days difference
CREATE temporary TABLE if not exists results AS (
select t.startdate, datediff(
(select min(t1.startdate)
from
date_table t1 where
t1.startdate>t.startdate),
t.startdate ) days_diff
from
date_table t) ;
select * from results:
Above query gives result as:
startdate days_diff
2018-08-22 2
2018-08-24 3
2018-08-27 2
2018-08-29 2
2018-08-31 5
2018-09-05 2
2018-09-07 3
2018-09-10
But I want result like:
startdate enddate days_diff
2018-08-22 2018-08-24 2
2018-08-27 2018-08-29 2
2018-08-31 2018-09-05 5
2018-09-07 2018-09-10 3
I'm using MySQL(version 5.6). Kindly let me know if there is any solution for this problem.
Thanks in advance.
You appear to want to enumerate the rows and then aggregate. In pre-8.0 versions, you can use variables. The rest is then aggregation:
select min(start_date) as start_date, max(start_date) as end_date
from (select (#rn := #rn + 1) as seqnum, t.*
from (select t.* from date_table t order by start_date) t cross join
(select #rn := 0) params
) t
group by floor((seqnum - 1) / 2);

Find max of continuous streak and the current streak from datetime

I have the following data of a particular user -
Table temp -
time_stamp
2015-07-19 10:52:00
2015-07-18 10:49:00
2015-07-12 10:43:00
2015-06-08 12:32:00
2015-06-07 11:33:00
2015-06-06 10:05:00
2015-06-05 04:17:00
2015-04-14 04:11:00
2014-04-02 23:19:00
So the output for the query should be -
Maximum streak = 4, Current streak = 2
Max streak = 4 because of these -
2015-06-08 12:32:00
2015-06-07 11:33:00
2015-06-06 10:05:00
2015-06-05 04:17:00
And current streak is 2 because of these (Assuming today's date is 2015-07-19)-
2015-07-19 10:52:00
2015-07-18 10:49:00
EDIT: I want a simple SQL query for MYSQL
For MAX streak(streak) you can use this, I have use the same query to calculate max streak. This may help you
SELECT *
FROM (
SELECT t.*, IF(#prev + INTERVAL 1 DAY = t.d, #c := #c + 1, #c := 1) AS streak, #prev := t.d
FROM (
SELECT date AS d, COUNT(*) AS n
FROM table_name
group by date
) AS t
INNER JOIN (SELECT #prev := NULL, #c := 1) AS vars
) AS t
ORDER BY streak DESC LIMIT 1;
A general approach with the gaps and islands queries is to tag each row with its rank in the data and with its rank in the full list of dates. The clusters will all have the same difference.
Caveats: I don't know if this query will be efficient. I don't remember if MySQL allows for scalar subqueries. I didn't look up the way to calculate a day interval in MySQL.
select user_id, max(time_stamp), count(*)
from (
select
t.user_id, t.time_stamp,
(
select count(*)
from T as t2
where t2.user_id = t.user_id and t2.time_stamp <= t.time_stamp
) as rnk,
number of days from t.time_stamp to current_date as days
from T as t
) as data
group by usr_id, days - rnk