Finding Records with Specific dateTime Difference in MySQL - mysql

I have an audit Trail table where I store the records with time taken for each transaction
Ex:
DateTime ReferenceID Status
2012-08-29 18:54:22 abc
2012-08-29 18:54:28 abc Success
2012-08-29 18:54:32 abcd
2012-08-29 18:54:48 abcd Success
I want to find out all the records that took more than say 10 seconds.
My output should look like
ReferenceID TimeTaken
abcd 16
Thanks
Soni

You can do a self join on the table from the records with blank Status to the corresponding Success record and calculate the difference between them. This query is assuming you only have the two records for each ReferenceID, if there may be more you will have to do some further filtering.
select d1.ReferenceID,
TimeStampDiff(SECOND, d1.Dttm, d2.Dttm) TimeTaken
from data d1
left join data d2 on d2.ReferenceID = d1.ReferenceID
and d2.Status = 'Success'
where d1.Status = ''
and TimeStampDiff(SECOND, d1.Dttm, d2.Dttm) > 10
SQL Fiddle

Related

How to retrieve Closest date from another table dynamically MySQL

I have two tables:
INFO table
ID LockDate Investor
157 10/15/2018 TEST1
VF1 09/02/2018 TEST2
LO2 05/01/2018 TEST3
09K 03/03/2012 TEST4
098 05/01/2012 TEST5
099 09/09/2012 TEST6
2YT 08/25/2012 TEST7
NUMBERS table
Dates Amount
10/10/2018 25.10
08/31/2018 200.15
05/10/2018 15.251
03/03/2012 10.10
05/10/2012 15.251
08/31/2012 548.0
I want the query to select all the values in the INFO table and find the closest or equal date in the Numbers table and give me the Amount. So my results would be:
ID LockDate Investor Amount
157 10/15/2018 TEST1 25.10
VF1 09/02/2018 TEST2 200.15
LO2 05/01/2018 TEST3 15.251
09K 03/03/2012 TEST4 10.10
098 05/01/2012 TEST5 15.251
099 09/09/2012 TEST6 548.0
2YT 08/25/2012 TEST7 548.0
By closest I mean equal or closest one whether it is lesser or greater than lock date.
This is the query I'm using but it is just retrieving the greater or equal one which doesn't work at all for me since I have to do it dynamically...
SELECT I.* ,
N.Amount FROM
(
SELECT A.*, MIN(NUM.Dates) AS XDATE
FROM INFO A
INNER JOIN NUMBERS AS NUM
ON NUM.Dates >= A.LockDate
GROUP BY A.ID
)AS RES
INNER JOIN NUMBERS AS N
ON N.Dates = I.XDATE
I will appreciate any help.
You just need to find the absolute minimum value for LockDate minus Dates. This will give you the closest date; lesser or greater. Rest is easy.
SELECT info.*, numbers.*
FROM info
INNER JOIN (
SELECT ID, MIN(DATEDIFF(GREATEST(LockDate, Dates), LEAST(LockDate, Dates))) Delta
FROM info
CROSS JOIN numbers
GROUP BY ID
) g ON info.ID = g.ID
INNER JOIN numbers ON DATEDIFF(GREATEST(LockDate, Dates), LEAST(LockDate, Dates)) = g.Delta
SQL Fiddle
Not sure what is your definition of "closest".
Here is my approach to get dates<=LockDate
http://sqlfiddle.com/#!9/8eea46/8
SELECT i.*, n.amount
FROM info i
LEFT JOIN numbers n
ON i.LockDate >= n.dates
LEFT JOIN numbers n_all
ON i.LockDate >= n_all.dates
AND n_all.dates > n.dates
WHERE n_all.dates IS NULL
Note: expected result is different since the definition of "closest" kind changed.
PS
Q: Why do I think that is a good approach?
A: Because whenever we deal with the data related to the timeline usually we expect data to know what (state, events, results) was before the moment we have on the timeline but not what will happen in future. That mean 31/12/2017 line/record can have/collect data from any table/records before 31/12/2017 but none from 2018. This strategy helps to keep reports consistent. They less dependent on the date when we generate the report. if we generate report about Dec 2017 at 1st Jan 2018 it will output same result as if we generate same report on same period Dec 2017 but week or month later 10th Jan 2018 or 1st of Feb.

SQL Select data from table when date is either specified or empty

I want to select data from my database when a particular column in a table has a specific data or it is not filled.
here is my sql code:
SELECT
bus.bus_id,
bus.seats,
entity.entityid,
entity.company_name,
entity.logo AS companylogo,
route.start_point,
route.drop_point,
route.boarding_time,
route.arrival_time,
FROM
loading_buses
INNER JOIN bus ON loading_buses.bus_id = bus.bus_id
INNER JOIN route ON loading_buses.route_id = route.route_id
INNER JOIN entity ON bus.entity = entity.entityid AND route.entity_id =
entity.entityid
WHERE
loading_buses.`current_date` ='23/1/2018' OR
loading_buses.`current_time` >= '00:00' AND
route.start_point = 'accra' AND
route.drop_point = 'kumasi'
ORDER BY
route.price ASC
And this is my output
SQL OUTPUT
1 49 1 STC assets/images/stc.png accra kumasi 1/17/2018 20:00 1/18/2018 00:00 20 9/18/2017 7/18/2017
2 15 1 STC assets/images/stc.png accra kumasi 1/17/2018 20:00 1/18/2018 00:00 20 9/18/2017 7/18/2017
3 55 1 STC assets/images/stc.png accra kumasi 1/17/2018 20:00 1/18/2018 00:00 20 9/18/2017 7/18/2017
In my database, the column current date is has certain date which must show depending on the date inserted or if no date in the column, it must show.
So in conclusion, result from the query show show only data for the specified date and null. But Query is displaying all data in the table
current_date is used as keyword to define system date in many databases. So, please make sure It is not the case here.
current_date ='23/1/2018' --- Change date value in this condition and see if it still returns all the data.
Also, comment out this condition --- loading_buses.current_time >= '00:00'
as all the rows satisfy this condition.
if you want check for null you should use is null
WHERE loading_buses.`current_date` ='23/1/2018' OR
loading_buses.`current_time` is null
Or based on your comment you can use
WHERE loading_buses.`current_date` ='23/1/2018' AND
loading_buses.`current_time` is null
You have to use () to group your OR's:
WHERE
(loading_buses.`current_date` ='2018-01-23' OR loading_buses.`current_date` is null)
AND
route.start_point = 'accra' AND
route.drop_point = 'kumasi'

If more than 10% of results are over X in mysql

I have a database table with lists of temperature readings from many locations in a number of buildings. I need a query that will give me a true or false if more than 10% of the readings in a building, taken on a date, are greater than X
I am not looking for a average. If there are 100 measurements taken in a building on a date, and 10 of them are over X (say 80 degrees) then create a flag.
The table is laid out as
Building # location # date temperature
| 123 | 555 |2016-04-08 | 68.5 |
| 123 | 556 |2016-04-08 | 70.2 |
| 123 | 557 |2016-04-08 | 65.4 |
| 888 | 999 |2013-03 22 | 80.4 |
Typically a building would have over 100 readings. There are many hundreds of building/date entries in the table
Can this be done with a single mysql query and can you share that query with me?
I obviously haven't made my question clear.
The result I am looking for is a single True or False.
If more than 10% of the results for a building/date combination were over X (say 80%) then show true, or some flag equal to true.
The known fields will be building and date. The location is not relevant, and can be ignored. So given the input of building (123) and date (2016-04-08) are more than 10% of the entries in the table that have that building number and date greater than X (e.g. 80). The only data to be tested are those for that building and date. So the query would end in:
where building_id=`123` AND date =`2016-04-08`
I am NOT looking for an average or a median. I am NOT looking to see a list of the data for that 10%. I am just looking for true or false.
You can use conditional aggregation, something like this:
select building, date,
(case when avg(temperature > x) > 0.1 then 'Y' else 'N' end) as flag
from t
group by building, date;
To return building and date, and "create a flag" for rows where more than 10% of the readings for that building on that date are over a given value X ...
SELECT r.building
, DATE(r.date)
, ( SUM(r.reading > X ) > SUM(.10) ) AS _flag
FROM myreadings r
GROUP BY r.building, DATE(r.date)
Absent more specification about the actual resultset you want to return, we're just guessing at what result set you want to return.
FOLLOWUP
Based on the update to the question... to return a row for a single building and a single date, add the WHERE clause as shown in the question. And remove expressions from the SELECT list.
SELECT ( SUM(r.reading > X ) > SUM(.10) ) AS _flag
FROM myreadings r
WHERE r.building = '123'
AND r.date >= '2016-04-08'
AND r.date < '2016-04-08' + INTERVAL 1 DAY
If there are no rows for the given building and given date, the query will return zero rows. If there is at least one row, and the number of rows that have a reading greater than X is more than 10% of the total number of rows, the query will return a single row, with _flag having a value of 1 (TRUE). Otherwise, the query will return a single row with _flag having a value of 0 (FALSE).
If you want the query to return a row even when there are no matching rows in the table, that can be accomplished with a more complex SQL statement.
If you want the query to return string values 'TRUE' or 'FALSE', that can be accomplished as well.
Again, absent an example of the resultset you are expecting to have returned, (without an actual specification which we can compare a resultset to), we're just guessing.

Mysql join query failure

For the tables below I need to output the rows that are not in the member_booking table from master_vehicle_inventory table.
I also need the results from master_vehicle_inventory which don't fall between the range of mb_startdate and mb_returndate.
master_vehicle_inventory table
mvi_id
1
2
3
4
member_booking table
mb_id mb_startdate mb_returndate mvi_id
100 22-04-2016 30-04-2016 2
101 23-01-2016 02-05-2016 3
So far I have tried this:
SELECT
mb.mb_id,
mb.mb_startdate,
mb.mb_returndate,
mvi.mvi_id
FROM master_vehicle_inventory AS mvi LEFT JOIN LEFT
JOIN member_booking AS mb ON mvi.mvi_id = mb.mvi_id
WHERE CURDATE() NOT BETWEEN mb.mb_startdate
AND mb.mb_returndate
AND mvi.mvi_id NOT IN (SELECT mvi.mvi_id
FROM member_booking)
But it doesn't give the results want.
To return rows from `master_vehicle_inventory` which do not have a "matching" row in `member_booking` table, we can use an anti-join pattern.
Assuming \mb_startdate` and `mb_enddate` are defined as datatype DATE.
Something like this:
SELECT mvi.mvi_id
FROM master_vehicle_inventory mvi
LEFT
JOIN member_booking mb
ON mb.mvi_id = mvi.mvi_id
AND mb.mb_startdate <= DATE(NOW())
AND mb.mb_enddate > DATE(NOW())
WHERE mb.mvi_id IS NULL
(The specification for a "matching" row is a bit vague.)
Given DATE(NOW()) returns '2016-04-25' we expect that query to return rows
mvi_id
------
1
4
(We are suspicious that the datatype of the columns may not be DATE because the values shown in the example data are not in the YYYY-MM-DD format we expect returned for a DATE expression.)

SQL Query Help - Grouping By Sequences of Digits

I have a table, which includes the following columns and data:
id dtime instance data dtype
1 2012-10-22 10000 d 1
2 2012-10-22 10000 d 1
..
7 2012-10-22 10004 d 1
..
15 2012-10-22 10000 # 1
16 2012-10-22 10004 d 1
17 2012-10-22 10000 d 1
I want to group sequences of 'd's in the data column, with the '#' at the end of the sequence.
This could have been done by grouping via the instance column, which is an individual stream of data, however there can be multiple sequences within the stream.
I also want to end a sequence if there are no data columns in the same instance for, say, 3 seconds after the last data of that instance and no '#'s have been found within that interval.
I have managed to do exactly this using cursors and while loops, which worked reasonably well for tables with 1000s of rows, however this query will be used on far more rows eventually, and these two methods would take around a minute with a dataset of just 3-5000 rows.
Reading on this website and others, it seems that set-based logic may be the way to go, however I can think of no way to do what I need without some kind of loop on each row that compares it to every other to build the 'sequences'.
If anyone could help, or point me in the direction of something that could, it would be greatly appreciated. :)
I would ideally like the data to be output in the following format:
datacount instance lastdata dtime
20 10000 # 2012-10-22
19 10000 d 2012-10-22
22 10004 # 2012-10-22
20 10022 # 2012-10-22
Where (datacount) is a count of the number of rows in a 'sequence' (which is the data leading up to a '#' or 3 second delay), (instance) is the instance ID from the original table, (lastdata) is the last data value in the sequence, (dtime) is the datetime value of the last data value.
Let me show you how to do this for the final '#'. The time difference follows a similar idea. The key idea is to get the next '#' after the current row. For this you need a correlated subquery. After that, you can do a group by:
select groupid, count(*) as NumInSeq, max(dtime) as LastDateTime
from (select t.*,
(select min(t2.id) from t t2 where t2.id > t.id and t2.data = '#'
) as groupid
from t
) t
group by groupid
Handling the time sequence is a bit more complicated. It is something like this:
select groupid, count(*) as NumInSeq, max(dtime) as LastDateTime,
(case when sum(case when data = '#' then 1 else 0 end) > 0 then '#' else 'd' end) as FinalData
from (select t.*,
(select min(t2.id)
from t t2
where t2.id > t.id and
(t2.data = '#' or UNIX_TIMESTAMP(t2.dtime) - UNIX_TIMESTAMP(t.dtime) < 3
) as groupid
from t
) t
group by groupid