Combining two count queries from same source - mysql

I have two queries that return a list of service items with a count. One is for the entire database, and the other is for a specified period. They work great individually, but I would like to optimize them into a single query.
The two queries are:
SELECT service_type, count(service_type) from qba_customers group by service_type order by count(service_type) desc
SELECT service_type, count(service_type) from qba_customers WHERE created_on BETWEEN '2013-01-01' AND '2013-06-30' group by service_type order by count(service_type) desc
I tried a few things unsuccessfully, below is what I thought would work initially:
SELECT service_type, COUNT(service_type) AS full_count, (count(service_type) WHERE created_on BETWEEN '2013-01-01' AND '2013-06-30') AS period_count FROM qba_customers GROUP BY service_type ORDER BY service_type DESC
Thanks in advance!

All aggregate functions, including COUNT, will only include a value if it's not NULL. To count just the rows that fall within a date range, use the CASE construct as the count argument. If the CASE returns a value, it's counted. If it doesn't return a value, it's not counted:
SELECT
service_type,
COUNT(service_type) AS GrandTotal,
COUNT(CASE WHEN created_on BETWEEN '2013-01-01' AND '2013-06-30' THEN 1 END) AS FirstHalf2013
FROM qba_customers
GROUP BY service_type
ORDER BY GrandTotal
For the FirstHalf2013 column, if created_on is within the target range the CASE returns a non-null value (I used 1 here, but it could be any non-null value). If created_on is not within the target range the CASE returns NULL which means the row is not counted.

Try it using the case statement:
SELECT service_type,
count(service_type) as full_count,
sum(case when created_on between '2013-01-01' and '2013-06-30' then 1 else 0 end) as period_count
FROM qba_customers
GROUP by service_type
ORDER by service_type desc

Related

MySQL SELECT SUM CASE with GROUP BY or DISTINCT

I'm trying to count unique user ids in a log table by month. So far I came up with the following query:
SELECT
COUNT(CASE WHEN log_date LIKE '2020-01%' THEN 1 END) AS januari
FROM user_log;
This query returns the total of all rows of the user_log in januari. However I would like to know how many unique users have logged in in Januari. So I need something like:
SELECT
COUNT(**DISTINCT user_id** CASE WHEN log_date LIKE '2020-01%' THEN 1 END) AS januari
FROM user_log;
I also tried GROUP BY, but so far no luck. Does anyone have a suggestion?
Consider:
SELECT COUNT(DISTINCT CASE WHEN log_date >= '2020-01-01' AND log_date < '2020-02-01' THEN userid END) AS januari
FROM user_log;
I changed the filtering logic to use half-open intervals rather than string matching: it is more efficient.
Note that, if you just that result for January, it is sufficient to use a WHERE clause:
SELECT COUNT(DISTINCT userid) januari
FROM user_log
WHERE log_date >= '2020-01-01' AND log_date < '2020-02-01'

I need a query that will get the sum(column) with 4 different where conditions which includes group by condition

Here's my query:
SELECT
date,
(SELECT sum(sessions) as org FROM datatwo WHERE medium='Organic Search' GROUP by date),
(SELECT sum(sessions) as soc FROM datatwo WHERE medium='Social' GROUP BY date),
(SELECT sum(sessions) as dir FROM datatwo WHERE medium='Direct' group by date),
(SELECT sum(sessions) as ref FROM datatwo WHERE medium='Referral')
FROM datatwo
GROUP BY date
I am trying to combine 4 queries in a single query but I am getting this error, I am using mysql DB.
1242 - Subquery returns more than 1 row
When you use the result of a query as a column, it must return exactly one row and one column. Expressions like this:
(SELECT sum(sessions) as org FROM datatwo WHERE medium='Organic Search' GROUP by date)
will return one row for every distinct date value.
I suspect you want this:
SELECT
date,
sum(case when medium='Organic Search' then sessions end) as org,
sum(case when medium='Social' then sessions end) as soc,
sum(case when medium='Direct' then sessions end) as dir,
sum(case when medium='Referral' then sessions end) as ref
FROM datatwo
GROUP BY 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

conversion mysql to postgresql

I have a working mysql query, but I can not get it work with postgres. This is the query (I already changed date format to to_char
SELECT country as grouper, date(users.created_at) as date,
to_char(users.created_at, '%Y-%m') as date_group,
count(id) as total_count
FROM "users"
WHERE (users.created_at >= '2011-12-01')
AND (users.created_at <= '2014-02-11')
GROUP BY grouper, date_group
ORDER BY date ASC
I am getting the error:
PG::Error: ERROR: column "users.created_at" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT country as grouper, date(users.created_at) as date, t...
Thank for your help.
SELECT country as grouper, date(MIN(users.created_at)) as date,
to_char(MIN(users.created_at), '%Y-%m') as date_group,
count(id) as total_count
FROM "users"
HAVING (users.created_at >= '2011-12-01')
AND (users.created_at <= '2014-02-11')
GROUP BY grouper, date_group
ORDER BY date ASC
MySQL is not very strict. In standard conform SQL all column values have to use an aggrate function (SUM, COUNT, MAX, MIN) on non-grouping fields - when using GROUP BY.
Honestly said, I am not entirely sure about data_group in the GROUP BY; can it be dropped?
Also note that I have switched WHERE with a HAVING.
You should use every selected column in GROUP BY section.
SELECT country as grouper, to_char(created_at, '%Y-%u') as date_group, count(id) as total_count
FROM "users"
WHERE created_at >= '2013-10-01'
AND created_at <= '2014-02-11'
GROUP BY grouper, date_group
ORDER BY date_group ASC

MySQL - How can I improve these queries?

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!