MySQL - How can I improve these queries? - mysql

first one:
SELECT MONTH(timestamp) AS d, COUNT(*) AS c
FROM table
WHERE YEAR(timestamp)=2012 AND Status = 1
GROUP BY MONTH(timestamp)
one of the issues I'm facing for this one is that I have to run multiple queries that use different values for Status. Is there a way to combine them into one? Like in one column it would have all the counts for when Status=1 and another column for when Status=2, etc.
second one:
SELECT COUNT(*) c , MONTH(timestamp) t FROM
(
SELECT t.adminid, timestamp
FROM table1 t
LEFT JOIN admins a ON a.adminID=t.adminID
WHERE YEAR(timestamp)=2012
GROUP BY t.adminID, DATE(Timestamp)
ORDER BY timestamp DESC
) AS a
GROUP BY MONTH(timestamp)
ORDER BY MONTH(timestamp) ASC;
a nested query, not sure if I can improve on this. I'm running this one on 2 tables, one has ~35k rows and one has ~300k rows. It takes about half a second for the first table and 4-5 seconds for the second.

These might help:
First one:
SELECT MONTH(timestamp) AS d,
sum(case when Status=1 then 1 else 0 end) as Status1Count,
sum(case when Status=2 then 1 else 0 end) as Status2Count,
sum(case when Status=3 then 1 else 0 end) as Status3Count
FROM `table`
WHERE timestamp between '2012-01-01 00:00:00' and '2012-12-31 23:59:59'
AND Status in (1,2,3)
GROUP BY MONTH(timestamp);
Second one:
Make sure that there is an index on the timestamp column and then make sure that you do not run any conversion functions e.g. MONTH(timestamp) on the indexed column. Somthing like:
SELECT COUNT(*) c , a.m as t FROM
(
SELECT t.adminid, timestamp, MONTH(timestamp) as m
FROM table1 t
LEFT JOIN admins a ON a.adminID=t.adminID
WHERE timestamp between '2012-01-01 00:00:00' and '2012-12-31 23:59:59'
GROUP BY t.adminID, DATE(Timestamp)
ORDER BY timestamp DESC
) AS a
GROUP BY a.m
ORDER BY a.m ASC;
Second one is a bit tricky since I do not have the data in front of me so I can't see the DB access path!

Related

Avg function not returning proper value

I expect this query to give me the avg value from daily active users up to date and grouped by month (from Oct to December). But the result is 164K aprox when it should be 128K. Why avg is not working? Avg should be SUM of values / number of current month days up to today.
SELECT sq.month_year AS 'month_year', AVG(number)
FROM
(
SELECT CONCAT(MONTHNAME(date), "-", YEAR(DATE)) AS 'month_year', count(distinct id_user) AS number
FROM table1
WHERE date between '2020-10-01' and '2020-12-31 23:59:59'
GROUP BY EXTRACT(year_month FROM date)
) sq
GROUP BY 1
Ok guys thanks for your help. The problem was that on the subquery I was pulling the info by month and not by day. So I should pull the info by day there and group by month in the outer query. This finally worked:
SELECT sq.day_month, AVG(number)
FROM (SELECT date(date) AS day_month,
count(distinct id_user) AS number
FROM table_1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY 1
) sq
GROUP BY EXTRACT(year_month FROM day_month)
Do not use single quotes for column aliases!
SELECT sq.month_year, AVG(number)
FROM (SELECT CONCAT(MONTHNAME(date), '-', YEAR(DATE)) AS month_year,
count(distinct id_user) AS number
FROM table1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY month_year
) sq
GROUP BY 1;
Note the fixes to the query:
The GROUP BY uses the same columns as the SELECT. Your query should return an error (although it works in older versions of MySQL).
The date comparisons have been simplified.
No single quotes on column aliases.
Note that the outer query is not needed. I assume it is there just to illustrate the issue you are having.

Combine 2 sql queries into 1 query

I have 2 queries right now for which I am looking to combine into 1 if possible.
I have open tickets stored in the Tickets_Open table and closed tickets in Tickets_Closed. Both tables have "Date_Requested" and "Date_Completed" columns. I need to count the number of tickets requested and completed each day.
My tickets requested count query is the following:
SELECT SUM(Count) AS TotalOpen, Date FROM(
SELECT COUNT(Ticket_Request_Code) AS Count, Date_Requested AS Date
FROM Tickets_Closed
WHERE Date_Requested >='2018-01-01 00:00:00'
GROUP BY(Date_Requested)
UNION
SELECT COUNT(Work_Request_Code) AS Count, Date_Requested AS Date
FROM Tickets_Open
WHERE Date_Requested >='2018-01-01 00:00:00'
GROUP BY(Date_Requested)
) AS t1 GROUP BY Date ORDER BY `t1`.`Date` DESC
My tickets completed count query is the following:
SELECT COUNT(Ticket_Request_Code) AS CountClosed, Date_Completed AS Date
FROM Tickets_Closed
Where Date_Completed >='2018-01-01 00:00:00'
GROUP BY(Date_Completed)
Both queries return the correct result. For open it returns with the column headings Date and TotalOpen. For close it returns with the column headings Date and CountClosed.
Is it possible to return it with the following column headings Date, TotalOpen, CountClosed.
You can combine these as:
SELECT Date, SUM(isopen) as isopen, SUM(isclose) as isclose
FROM ((SELECT date_requested as date, 1 as isopen, 0 as isclose
FROM Tickets_Closed
WHERE Date_Requested >= '2018-01-01'
) UNION ALL
(SELECT date_requested, 1 as isopen, 0 as isclose
FROM Tickets_Open
WHERE Date_Requested >= '2018-01-01'
) UNION ALL
(SELECT date_closed as date, 0 as isopen, 1 as isclose
FROM Tickets_Closed
WHERE date_closed >= '2018-01-01'
)
) t
GROUP BY Date
ORDER BY Date DESC;
This assumes that Ticket_Request_Code and Work_Request_Code are not NULL. If COUNT() is really being used to check for NULL values, then add the condition to the WHERE clause in each subquery.
This query uses the FULL OUTER JOIN on the Dates as well, but it correctly adds the Open/Closed counts together to give you a TotalOpen Count. This will also handle possible NULL values for cases where you have a day that doesn't close any tickets.
WITH open AS
(
SELECT COUNT(Work_Request_Code) AS OpenCount, Date_Requested AS Date
FROM Tickets_Open
WHERE Date_Requested >='2018-01-01 00:00:00'
GROUP BY(Date_Requested)
)
, close AS
(
SELECT COUNT(Ticket_Request_Code) AS ClosedCount, Date_Requested AS Date
FROM Tickets_Closed
WHERE Date_Requested >='2018-01-01 00:00:00'
GROUP BY(Date_Requested)
)
SELECT
COALESCE(c.Date, o.Date) AS Date
, IFNULL(o.OpenCount, 0) + IFNULL(c.ClosedCount, 0) AS TotalOpen
, IFNULL(c.CountClosed, 0) AS CountClosed
FROM open o
FULL OUTER JOIN closed c ON o.Date = c.Date

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

Reverse order for GROUP BY in MySQL

I need to select first value for every hour from my db. But I don't know how to reverse order on GROUP BY statement.
How can i rewrite my query (now it selects last value in hour)?
SELECT HOUR(`time`) as hour, mytable.*
FROM mytable
WHERE DATE(`time`) ="2015-09-12" GROUP BY HOUR(`time`) ORDER BY `time` ASC;
This query gave me expected result:
SELECT HOUR(`time`) as hour, sortedTable.* FROM
(SELECT electrolysis.* FROM electrolysis
WHERE DATE(`time`)='2015-09-12' ORDER BY `time`) as sortedTable
GROUP BY HOUR(`time`);
You can just select the MIN HOUR in sub query , try using the query:
SELECT * from mytable WHERE `time` IN (
SELECT MIN(HOUR(`time`)) as `hour`
FROM mytable
WHERE DATE(`time`) ="2015-09-12"
GROUP BY HOUR(`time`) ) ORDER BY `time` ASC;
You can do something like this:-
SELECT sub0.min_time,
mytable.*
FROM mytable
INNER JOIN
(
SELECT MIN(`time`) AS min_time
FROM mytable
GROUP BY HOUR(`time`)
) sub0
ON mytable.`time` = sub0.min_time
WHERE DATE(`time`) ="2015-09-12"
ORDER BY `time` ASC
This is using a sub query to get the smallest time in each hour. This is then joined back against your main table on this min time to get the record that has this time.
Note that there is a potential problem here if there are multiple records that share the same time as the smallest one for an hour. There are ways around this, but that will depend on your data (eg, if you have a unique id field which is always ascending with time then you could select the min id for each hour and join based on that)
You can use below query, which is more optimized just make sure that time field should be indexed.
SELECT HOUR(m.time), m.*
FROM mytable AS m
JOIN
(
SELECT MIN(`time`) AS tm
FROM mytable
WHERE `time` >= '2015-09-12 00:00:00' AND `time` <= '2015-09-12 23:59:59'
GROUP BY HOUR(`time`)
) AS a ON m.time=a.tm
GROUP BY HOUR(m.time)
ORDER BY m.time;

Multiple Select Subquery Count based on Hour of Day, Would Like to Add Table/Column

Right now, I have a multiple select subquery that is grabbing data based on hour of the day that's a count. What I want to do now, is to introduce another table into that query, and count based on an id as well as the datetime in the original table.
What I have right now is:
select
(
select count(a_date)
from t1
where d_date
between '2013-01-07 00:00:00' and '2013-01-07 00:59:59'
) AS '00:00 to 00:59',
(
select count(a_date)
from t1
where d_date
between '2013-01-07 01:00:00' and '2013-01-07 01:59:59'
) AS '01:00 to 01:59'
and so on, till the end of the day.
I have another query that's giving me the count based on the id and datetime, but there's only two columns, one which is showing the c_name and the other showing the count for the hour.
Ex.
select t2.c_name, count(t1.a_date)
from t2 join t1
on t2.t1_key = t1.t2_key
where t1.d_date
between '2013-01-07 00:00:00' and '2013-01-07 00:59:59'
group by t2.c_id
Basically, I'd like to combine these two queries into one that can show the c_name and all of the hours of the day.
Any suggestions?
I would look into using the CASE statement.
Try something like this (adding your additional 23 columns):
select c_name,
SUM(case when HOUR(d_date) = 0 then 1 else 0 end) '00:00 to 00:59',
SUM(case when HOUR(d_date) = 1 then 1 else 0 end) '01:00 to 01:59'
from t2
join t1 on t2.t1_key = t1.t2_key
group by c_name
And here is the SQL Fiddle.
You just need to add your WHERE criteria for d_date -- something like:
where d_date between '2013-01-07 00:00:00' and '2013-01-07 23:59:59'
or
where Date(d_date) = '2013-01-07'
Good luck!