MySql select relative similar dates - mysql

I trying to select records from a table that are in the same time window, for example
record_date | record_data
4/20/2015 5:00:00 PM | 23
4/20/2015 5:08:00 PM | 3
4/20/2015 5:09:00 PM | 98
if i set 2 minutes window will be result in:
4/20/2015 5:08:00 PM | 3
4/20/2015 5:09:00 PM | 98
but, if i choose 15 minutes window must get:
4/20/2015 5:00:00 PM | 23
4/20/2015 5:08:00 PM | 3
4/20/2015 5:09:00 PM | 98
how can be done that ? BETWEEN or DATEDIFF statements needs a absolute date to compare with, and in this case the comparison must be relative to other records values and not to an external time.

Mysql isn't very good at this (which means - there's nothing built in for it).
What we need to do, is associate a rank with each row, based on record_date order. ie, first row gets a rank of 1, the immediate next row by date gets a rank of 2, and so forth.
If we do that, we can then compare one row to the next fairly easily (ie compare rank to rank + 1)
This will give us pairings of rows, and the immediate next row by date, which we can then filter for those that are separated by your window of choice.
In order to transform that result, into a two column result, we need to repeat the query, union the results together, taking the first date and data in the first query, and the second date and data in the second query.
union is by default distinct, so we dont get any repeated rows. This gives us the following query:
select * from (
select t1.record_date, t1.record_data from
(select #rank := #rank + 1 as rank, records.*
from records, (select #rank := 0) q order by record_date asc) t1
left join
(select #rank2 := #rank2 + 1 as rank, records.*
from records, (select #rank2 := 0) q order by record_date asc) t2
on t1.rank + 1 = t2.rank
where t2.record_date < t1.record_date + interval 2 minute
union
select t2.record_date, t2.record_data from
(select #rank := #rank + 1 as rank, records.*
from records, (select #rank := 0) q order by record_date asc) t1
left join
(select #rank2 := #rank2 + 1 as rank, records.*
from records, (select #rank2 := 0) q order by record_date asc) t2
on t1.rank + 1 = t2.rank
where t2.record_date < t1.record_date + interval 2 minute
) q
order by record_date asc;
with a demo here
Or alternatively, you can do it in a slightly less over-the-top manner like this:
select *
from (
select r1.record_date, r1.record_data
from records r1
inner join records r2
on r2.record_date = (select min(record_date) from records where record_date > r1.record_date)
where r2.record_date < r1.record_date + interval 2 minute
union
select r2.record_date, r2.record_data
from records r1
inner join records r2
on r2.record_date = (select min(record_date) from records where record_date > r1.record_date)
where r2.record_date < r1.record_date + interval 2 minute
) q
order by record_date asc;
Where we dont both with ranking, and just join directly to the next sequential date via a subquery that finds it.
Updated demo here

Related

How to find first room having 2 days free slots between consecutive reservations

I have table like that:
room_id date_reservartion
101 October, 01 2016
101 October, 03 2016
102 October, 02 2016
102 October, 05 2016
103 October, 01 2016
103 October, 02 2016
103 October, 04 2016
104 October, 04 2016
I try find first room with free 2 days free in row.
In this situation answer is room 102 because have reservation on 2-10-2016 and 5-10-2016, 3-10 and 4-10 is free.
Could you help me?
Link to SQL Fiddle
Please try this.
Select top 1 * from (
Select a.room_id,DATEDIFF(DAY,a.date_reservartion,b.date_reservartion)-1 as Diff
FROM room as a
INNER JOIN room as b on a.room_id=b.Room_id
) as t Where Diff = 2
Order by room_id
It is showing 102 see..
I am sharing two of my approaches.
Approach #1:
SELECT
a.room_id
FROM
booking a
INNER JOIN booking b ON a.room_id = b.room_id
AND a.date_reservartion < b.date_reservartion
WHERE
NOT EXISTS (
SELECT
*
FROM booking c
WHERE c.room_id = a.room_id
AND c.date_reservartion BETWEEN
(a.date_reservartion + INTERVAL 1 DAY) AND
(b.date_reservartion - INTERVAL 1 DAY)
)
AND DATEDIFF(b.date_reservartion,a.date_reservartion) = 3
ORDER BY a.date_reservartion
LIMIT 1
Approach #2:
Here's another way you can achieve this using MySQL user defined variables.
SELECT
a.room_id
FROM
(
SELECT
*,
IF(#prevRoom = room_id, #rn := #rn + 1,
IF(#prevRoom := room_id, #rn := 1, #rn := 1)
) AS rn
FROM booking
CROSS JOIN (SELECT #prevRoom := 0, #rn := 1) AS var
ORDER BY room_id, date_reservartion
)AS a
INNER JOIN
(
SELECT
*,
IF(#prevRoom2 = room_id, #rn2 := #rn2 + 1,
IF(#prevRoom2 := room_id, #rn2 := 1, #rn2 := 1)
) AS rn
FROM booking
CROSS JOIN (SELECT #prevRoom2 := 0, #rn2 := 1) AS var
ORDER BY room_id, date_reservartion
) AS b
ON a.room_id = b.room_id AND a.rn = b.rn+1
WHERE DATEDIFF(a.date_reservartion,b.date_reservartion) = 3
SEE DEMO
Note:
If you need only the first free room of that kind then add the following two lines in the above query:
ORDER BY a.date_reservartion
LIMIT 1
Every room is free after 2016-10-05, here is an sql that shows when room is free for 2 days. You can easy modify it to see any free period (+ interval N day). If you apply limit 1, you will see the first free room.
select
room_id,
date_reservartion + interval 1 day AS from_date,
date_reservartion + interval 2 day AS to_date
from booking as b
where not exists (
select 1
from booking as b2
where b2.room_id = b.room_id
and b2.date_reservartion >= b.date_reservartion + interval 1 day
and b2.date_reservartion <= b.date_reservartion + interval 2 day
)
order by from_date ASC
Try This
Select top 1 a.room_id,DATEDIFF(DAY,a.date_reservartion,b.date_reservartion)-1 as Diff
FROM room as a
INNER JOIN room as b on a.room_id=b.room_id Where Diff = 2 Order by a.room_id
Please Try This
SELECT top 1 room_id
FROM (SELECT a.*, (b.date_reservartion - a.date_reservartion - 1) diff
FROM (SELECT ROW_NUMBER () OVER (ORDER BY room_id) rn, room_id,
date_reservartion
FROM booking) a inner join
(SELECT ROW_NUMBER () OVER (ORDER BY room_id) rn, room_id,
date_reservartion
FROM booking) b
On a.room_id = b.room_id AND a.rn = b.rn - 1) t
WHERE diff = 2
It is Showing 102

count consecutive days (streak) and number of records for current day

The following code was taken from another question on SO. Original Q&A
I would like to count the number of consecutive days (Streak) with records since today AND also how many records were made today. I'm using this to send notifications. If a user submits a new record the same day, they should not get a second notification telling them that they are on a streak (they were made aware the first time they submitted a record for the current day).
I tried adding a COUNT() function before #streak, after the first SELECT and pretty much everywhere that seemed reasonable but this query is too complex for me to figure it out.
SELECT streak + 1 as realStreak
FROM (
SELECT dt,
#streak := #streak+1 streak,
datediff(curdate(),dt) diff
FROM (
SELECT distinct date(dt) dt
FROM glucose where uid = 1
) t1
CROSS JOIN (SELECT #streak := -1) t2
ORDER BY dt desc
)
t1 where streak = diff
ORDER BY streak DESC LIMIT 1
http://sqlfiddle.com/#!9/45d386/1/0
The result of the above should be:
realStreak | RecordsToday
3 | 3
Just add a subquery for the today check
SELECT streak + 1 as realStreak,cdt
FROM (
SELECT dt,
#streak := #streak+1 streak,
datediff(curdate(),dt) diff
FROM (
SELECT distinct date(dt) dt
FROM gl where uid = 1
) t1
CROSS JOIN (SELECT #streak := -1) t2
ORDER BY dt desc
)t1
JOIN
(SELECT COUNT(CASE WHEN DATE(dt)=CURDATE() THEN 1 END) cdt FROM gl)x
where streak = diff
ORDER BY streak DESC LIMIT 1

Stop query when SUM is reached (mysql)

I have a database with colums I am working on. What I am looking for is the date associated with the row where the SUM(#) reaches 6 in a query. The query I have now will give the date when the number in the colum is six but not the sum of the previous rows. example below
Date number
---- ------
6mar16 1
8mar16 4
10mar16 6
12mar16 2
I would like to get a query to get the 10mar16 date because on that date the number is now greater than 6. Earlier dates wont total up to six.
Here is an example of a query i have been working on:
SELECT max(date) FROM `numbers` WHERE `number` > 60
You could use this query, which tracks the accumulated sum and then returns the first one that meets the condition:
select date
from (select * from mytable order by date) as base,
(select #sum := 0) init
where (#sum := #sum + number) >= 6
limit 1
SQL Fiddle
Most databases support ANSI standard window functions. In this case, cumulative sum is your friend:
select t.*
from (select t.*, sum(number) over (order by date) as sumnumber
from t
) t
where sumnumber >= 10
order by sumnumber
fetch first 1 row only;
In MySQL, you need variables:
select t.*
from (select t.*, (#sumn := #sumn + number) as sumnumber
from t cross join (select #sumn) params
order by date
) t
where sumnumber >= 10
order by sumnumber
fetch first 1 row only;
Awesome!!!! It seems to be working great. Here is the code that I used.
SELECT date, id, crewname
FROM (select * FROM flightrecord WHERE `crewname` = 'brayn'
ORDER BY dutyTimeArrive DESC) as base,
(select #sum := 0) init
WHERE (#sum := #sum + tankDropCount) >= 6
limit 1

SQL sum hits per day and calculate percentage change

I have a single table with a list of hits/downloads, every row has of course a date.
I was able to sum all the rows grouped by day.
Do you think it's possible to also calculate the change in percentage of every daily sum compared to the previous day using a single query, starting from the entire list of hits?
I tried to do this
select *, temp1.a-temp2.b/temp1.a*100 as percentage from
(select DATE(date), count(id_update) as a from vas_updates group by DATE(date)) as table1
UNION
(select DATE_ADD(date, INTERVAL 1 DAY), count(id_update) as b from vas_updates group by DATE(date)) as table2, vas_updates
but it won't work (100% CPU + crash).
Of course I can't JOIN them because those two temp tables share nothing in common being with 1 day offset.
The table looks like this, nothing fancy.
id_updates | date
1 2014-07-06 12:45:21
2 2014-07-06 12:46:10
3 2014-07-07 10:16:10
and I want
date | sum a | sum b | percentage
2014-07-07 2 1 -50%
It can be either be positive or negative obviously
select DATE(v.date), count(v.id_update) a, q2.b, count(v.id_update) - q2.b/count(v.id_update)*100 as Percentage
from vas_updates v
Left Join (select DATE_ADD(date, INTERVAL 1 DAY) d2, count(id_update) as b
from vas_updates group by d2) as q2
ON v.date = q2.d2
group by DATE(v.date)
The sum by day is:
select DATE(date), count(id_update) as a
from vas_update
group by DATE(date);
In MySQL, the easiest way to get the previous value is by using variables, which looks something like this:
select DATE(u.date), count(u.id_update) as cnt,
#prevcnt as prevcnt, count(u.id_update) / #prevcnt * 100,
#prevcnt := count(u.id_update)
from vas_update u cross join
(select #prevcnt := 0) vars
group by DATE(u.date)
order by date(u.date);
This will generally work in practice, but MySQL doesn't guarantee the ordering of variables. A more guaranteed approach looks like:
select dt, cnt, prevcnt, (case when prevcnt > 0 then 100 * cnt / prevcnt end)
from (select DATE(u.date) as dt, count(u.id_update) as cnt,
(case when (#tmp := #prevcnt) is null then null
when (#prevcnt := count(u.id_update)) is null then null
else #tmp
end) as prevcnt
from vas_update u cross join
(select #prevcnt := 0, #tmp := 0) vars
group by DATE(u.date)
order by date(u.date)
) t;

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