MYSQL loop this code - mysql

I want the following code to run with multiple end dates. those end dates must al be the first day of the month.
I can execute the code several times while changing the end date myself but thats to much work of i want montly data from 2001 until now. So my guess is I have to loop this?
Here is the code i'm using:
Select t4.Count, t4.Status
From(
SELECT count(l.VoerID) as Count, l.Datum, l.Status, l.LogID
FROM (
SELECT k.VoerID, k.Datum, MAX(k.LogID) AS LogID
FROM DB.LogStatus k
Where Datum > '2001-01-01'
and Datum < '2013-07-01'
GROUP BY k.VoerID
) m
JOIN DB.LogStatus l
ON l.VoerID = m.VoertuigID AND l.LogID = m.LogID
Where status in ('x',y,'z')
Group by Status
)t4
Who can help?
EDIT::::: #STEPH
When I use this peace of code (1):
SELECT VoertID,max(LogID) as MaxLogID,Datum
from DB.LogStatus
WHERE Datum >= '2001-01-01'
and Datum < '2013-07-01'
and VoerID = '50789'
GROUP BY VoerID
i get VoerID 50789 with the last LogID but not the corresponding date. hows that possible?

Based on your output, you need to manipulate your date to always return the first of the month.
In the SQL below, I have presented a moethod to do so that involves working out how many days past the begining of the month (relative to each date) there has been and substracting it.
SELECT date_sub(Datum,interval day(Datum)-1 day) datum, status, count(l.VoerID) as count
FROM DB.LogStatus l
INNER JOIN (SELECT VoerID,max(LogID) as MaxLogID
from DB.LogStatus
WHERE Datum >= '2001-01-01'
and Datum < '2013-07-01'
GROUP BY VoerID) maxl on l.VoerID=Maxl.VoerID and l.LogID=Maxl.MaxLogID
Where status in ('x','y','z')
and Datum >= '2001-01-01'
and Datum < '2013-07-01'
GROUP BY date_sub(Datum,interval day(Datum)-1 day),status
Breaking down your original code:
Return for a given VoerID, return the FIRST Date and MAX LogID
SELECT k.VoerID, k.Datum, MAX(k.LogID) AS LogID
FROM DB.LogStatus k
Where Datum > '2001-01-01'
and Datum < '2013-07-01'
GROUP BY k.VoerID
Get all items from the log where the status is in a range and where the LogIDs match and the VoerID in the table matches the VoertuigID from the derived table (not actually present in code). Group by log status and get FIRST Datum, FIRST LogID and the COUNT
SELECT count(l.VoerID) as Count, l.Datum, l.Status, l.LogID
FROM ( step1 ) m
JOIN DB.LogStatus l
ON l.VoerID = m.VoertuigID AND l.LogID = m.LogID
Where status in ('x',y,'z')
Group by Status
Return Count and Status columns from 2
The revised code does:
Identifies last LogID for each VoerID within certain dates
SELECT VoerID,max(LogID) as MaxLogID
from DB.LogStatus
WHERE Datum >= '2001-01-01'
and Datum < '2013-07-01'
GROUP BY VoerID
Restricts log by these max LogIDs, then filters on relevant statuses, and then counts the number of rows per status per first of month
SELECT date_sub(Datum,interval day(Datum)-1 day) datum, status, count(l.VoerID) as count
FROM DB.LogStatus l
INNER JOIN ( m ) maxl on l.VoerID=Maxl.VoerID and l.LogID=Maxl.MaxLogID
Where status in ('x','y','z')
and Datum >= '2001-01-01'
and Datum < '2013-07-01'
GROUP BY date_sub(Datum,interval day(Datum)-1 day),status

Related

How do I select data going back a period of time for each group?

I want for each group id to get the latest week's worth of data. Not from a specific date, but counting backwards from the MAX(startTime) of each individual group.
However, the following does not seem to work. I assume it's because startTime in each group (a single value) is BETWEENed by itself? Otherwise, how do I keep it in my filter?
SELECT
id
, startTime
FROM MyTable
GROUP BY id, startTime
HAVING startTime BETWEEN MAX(startTime) - INTERVAL 1 WEEK AND MAX(startTime)
What's the right query?
Also, in my case it has to work with MySQL 5.7.
SELECT m.id, m.startTime
FROM (
SELECT id, MAX(startTime) AS startTime
FROM MyTable GROUP BY id
) AS x
JOIN MyTable AS m ON m.id = x.id
AND m.startTime BETWEEN x.startTime - INTERVAL 1 WEEK AND x.startTime;

Optimise query with Group By and Count

Would like advice to optimise the query below, as it currently takes about 20 seconds to process on first run, depending on the amount of users being selected and the date range.
The purpose is to return a count of each code, per user.
There are 27 codes and each user has an am and pm attendance record.
SELECT user,
code,
COUNT(code) AS total
FROM attendances AS attendances
WHERE attendances.user IN ('abc123', 'abc456', 'abc789')
AND (
attendances.date >= '2019-10-06' AND attendances.date <= '2019-10-11'
)
GROUP BY user, code
Table Definiton
Indexes
Each of these fields has an index
location
source_id
date
user_recorded
code
user
EXPLAIN Result
For this query:
SELECT user, code,
COUNT(code) AS total
FROM attendances a
WHERE a.user IN ('abc123', 'abc456', 'abc789') AND
a.date >= '2019-10-06' AND a.date <= '2019-10-11'
GROUP BY user, code;
You have a problem. An index on both user and date will not be used in an index (in MySQL).
One method is union all with an index on (user, date, code):
(SELECT user, code, COUNT(*) AS total
FROM attendances a
WHERE a.user = 'abc123'
a.date >= '2019-10-06' AND a.date <= '2019-10-11'
GROUP BY user, code
) UNION ALL
(SELECT user, code, COUNT(*) AS total
FROM attendances a
WHERE a.user = 'abc456'
a.date >= '2019-10-06' AND a.date <= '2019-10-11'
GROUP BY user, code
) UNION ALL
(SELECT user, code, COUNT(*) AS total
FROM attendances a
WHERE a.user = 'abc789'
a.date >= '2019-10-06' AND a.date <= '2019-10-11'
GROUP BY user, code
);
This can make direct use of the index and should be much faster.

Totalling query in last row

This query will return a list of engineer names with test results for what they have tested in the last hour, what is faulty, what's is working and the total for each engineer.
I want to be able to add a row at the bottom which will total these amounts but am struggling, any one have any suggestions?
select distinct qcheck.checkby,
ifnull(fully,0) as fully,
ifnull(faulty,0) as faulty,
ifnull(lasthour,0) as lasthour,
ifnull(total,0) as total
from qcheck
left join (
select count(*) AS fully,
checkby,
qcheck.id
from qcheck
where result = 'fully tested & working'
and date(finishdate) = CURDATE()
group by checkby) AS fw
on fw.checkby=qcheck.checkby
left join (
select count(*) AS faulty,
checkby,
qcheck.id
from qcheck
where result = 'faulty'
and date(finishdate) = CURDATE()
group by checkby) AS ff
on ff.checkby=qcheck.checkby
left join (
select count(*) AS Lasthour,
checkby,
qcheck.id from qcheck
where finishdate >= now() - interval 1 hour
group by checkby) AS lh
on lh.checkby=qcheck.checkby
left join (
select count(*) AS total,
checkby,
qcheck.id from qcheck
where date(finishdate) = CURDATE()
group by checkby) AS total
on total.checkby=qcheck.checkby
where date(finishdate) = CURDATE()
and qcheck.checkby not like 'michael'
and qcheck.checkby not like 'chaz'
group by qcheck.checkby
order by total desc
First of all, you don't need the sub queries, you can instead do a count on a condition.
The with rollup modifier can be added to the group by clause to include the grand total. The order by cannot be used in the same query then, but can be applied in an outer query.
Furthermore, with the use of coalesce you can replace the null value for that total row with the label of your choice.
Finally, to still sort the total row at the end, you could add an is null expression in the order by clause, which will evaluate to false or true. The latter is ordered last.
select coalesce(checkby, 'Total') as checkby_or_total,
fully,
faulty,
lasthour,
total
from (
select qcheck.checkby,
count(case result when 'fully tested & working' then 1 end) as fully,
count(case result when 'faulty' then 1 end) as faulty,
count(case when finishdate >= now()-interval 1 hour then 1 end) as lasthour,
count(*) as total
from qcheck
where date(finishdate) = CURDATE()
and qcheck.checkby not like 'michael'
and qcheck.checkby not like 'chaz'
group by qcheck.checkby with rollup
) as main
order by checkby is null,
total desc

SQL: Changing structure of query

I have a query which successfully returns a single value where "day" is hardcoded in the query, as below:
SELECT MAX(theCount) FROM
(SELECT FK_Hour, Count(FK_Hour) As theCount FROM
(Select FK_Hour
From slottime
INNER JOIN time ON slottime.FK_Hour = time.Hour
WHERE FK_Hour IN
(SELECT time.Hour FROM time WHERE time.day=0 )
) As C
GROUP By FK_Hour
) AS counts;
I'm trying to remove this hardcoding such that two columns; namely
day:theCount are returned.
I have tried
SELECT MAX(theCount), day FROM
(SELECT FK_Hour, day As day, Count(FK_Hour) As theCount FROM
(Select slottime.FK_Hour, time.day
From slottime
INNER JOIN time ON slottime.FK_Hour = time.Hour
) As C
GROUP By FK_Hour
) AS counts
GROUP By day;
and it executes. However the values it returns are obviously incorrect (no obvious correlation to the data in the tables being queried)
In the first example, if I am reading this right, you are getting the count of records for day = 0 for each fk_hour value, and selecting the max count from that.
To do this for all days, start by writing an aggregation query to get the count of each day and hour pair like this:
SELECT t.day, t.hour, COUNT(*) AS numRecords
FROM time t
GROUP BY t.day, t.hour;
Once you have that, you can get the maximum value by using limit 1:
SELECT t.day, t.hour, COUNT(*) AS numRecords
FROM time t
GROUP BY t.day, t.hour
ORDER BY COUNT(*) DESC
LIMIT 1;
This query will return one row, and tell you at which day and which hour the highest number of records occurred.

MySQL AVG of TIMESTAMPDIFF function with WHERE CLAUSE

I have a table from which I am trying to get the average duration. I am using the following code
/*TIME DURATION*/
SELECT DATE(createDate),
AVG(TIMESTAMPDIFF(SECOND, min(createDate), max(createDate))) AS Duration
FROM Impressions
WHERE session_id IN (
SELECT session_id
FROM carts
WHERE createDate >= '2014-06-30'AND createDate < '2014-07-07'
AND HOUR(createDate) >= 10
AND HOUR(createDate) < 21
)
AND session_Id <> ''
GROUP BY DATE(createDate);
However, I am getting the following error and cannot understand why. Any help will be appreciated.
ErrorCode: -2147467259, Number: 1111
ErrorMessage: Invalid use of group function
I would recommend you to use the query like this:
SELECT DATE(tbl.createDate),
AVG(TIMESTAMPDIFF(SECOND, tbl.minDt, tbl.maxDt)) AS Duration
FROM (SELECT DATE(i.createDate) as createDate,
min(i.createDate) minDt,
max(i.createDate) maxDt
FROM Impressions i INNER JOIN
carts c ON (i.session_id = c.session_id)
WHERE c.createDate >= '2014-06-30'
AND c.createDate < '2014-07-07'
AND HOUR(c.createDate) >= 10
AND HOUR(c.createDate) < 21
AND i.session_Id <> ''
GROUP BY DATE(i.createDate) ) as tbl
GROUP BY DATE(tbl.createDate);