SQL trick count number of rows by months? - mysql

I have an elementary SQL query:
SELECT MONTH(created_at), COUNT(id) as total FROM `clients` GROUP BY MONTH(created_at)
It returns me data groupped by month like as:
MONTH(created_at) | total
09 1
10 2
How to fill remaining months to zero? So, in result I need to get all months:
MONTH(created_at) | total
09 1
.. 2
12 5
I tried this way:
SELECT months.id, COUNT(clients.id) as total FROM `months` LEFT JOIN `clients` ON MONTH(created_at) = months.id GROUP BY MONTH(created_at)

Use a derived table with all month numbers and left join your table on to that.
SELECT mths.mth, COUNT(c.id) as total
FROM (select 1 as mth union select 2 union select 3 union
select 4 union select 5 union select 6 union select 7 union
select 8 union select 9 union select 10 union select 11 union select 12) mths
LEFT JOIN `clients` c on mths.mth=month(c.created_at)
GROUP BY mths.mth

The best practice is to have CALENDAR table, from which you can query period you need and then left join table with data.
Or you can simply generate list of periods you need. In case you have a small period, you can use derived table. The fastest way in this case would be excel-generated list.

Thank all for answers, especially Alex, this way works:
SELECT months.id, COUNT(clients.id) as total FROM `months` LEFT JOIN `clients` ON months.id = MONTH(created_at) GROUP BY months.id;

Related

MySQL query to count zero value using group by in the same table

Here's my "customers" table:
To get number of enquiries per for a particular month and year, I'm using following query:
SELECT YEAR(customer_date) AS Year, MONTH(customer_date) AS Month, COUNT(customer_id) AS Count FROM customers WHERE customer_product = 6 GROUP BY YEAR(customer_date), MONTH(customer_date)
I get following result:
You can see that as there is no enquery in the April month, so no row fetched for month number 4. But I want 0 value in Count column if there is no record found in that particular month and year.
This is what I want:
One option uses a calendar table to represent all months and years, even those which do not appear in your data set:
SELECT
t1.year,
t2.month,
COUNT(c.customer_id) AS Count
FROM
(
SELECT 2017 AS year UNION ALL
SELECT 2018
) t1
CROSS JOIN
(
SELECT 1 AS month UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12
) t2
LEFT JOIN customers c
ON t1.year = YEAR(c.customer_date) AND
t2.month = MONTH(c.customer_date)
WHERE
c.customer_product = 6
GROUP BY
t1.year,
t2.month
ORDER BY
t1.year,
t2.month;
Note: The above query can probably be made faster by actually creating dedicated calendar tables in your MySQL schema.
The following index on the customers table might help:
CREATE INDEX idx ON customers(customer_product, customer_id);
This might make the join between the calendar tables and customers faster, assuming that the customer_product = 6 condition is restrictive.

MySQL avg count per month for current year (include months with no data)

I am trying to create a query for a bar-chart which displays a monthly overview of the number of orders.
The query I am using is correctly providing me with the breakdown per month but when I skipped a month, it is not providing a 0 for that month, just doesnt add it at all.
Since this chart is expecting 12 numbers, everything after the missing month would be off an inaccurate.
Current Attempt:
select Month(dateCreated) as monthID,
Monthname(dateCreated) as monthName,
count(dateCreated) as totalRewards
from reward
where Year(dateCreated) = '2018'
GROUP BY monthID
If we were to assume that it is currently May 2018, I would like to see Jan - May, current counts even if the month had no orders (April = 0).
Whats the best way to include all months that have happened so far in the provided year and then their appropriate count?
You can mock a months table, then LEFT JOIN the reward table against it. To ensure you only get valid results, it's best to use a SUM() where not null rather than a COUNT() aggregate:
SELECT
months.id as monthID,
MONTHNAME(CONCAT('2018-',months.id,'-01')) as monthName,
SUM(CASE WHEN dateCreated IS NULL THEN 0 ELSE 1 END) as totalRewards
FROM
(
SELECT 1 AS id
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11
UNION SELECT 12
) as months
LEFT JOIN reward
ON MONTH(reward.dateCreated) = months.id
AND YEAR(dateCreated) = '2018'
GROUP BY monthID, monthName
ORDER BY monthID;
SQL Fiddle

Order By month and Year for 2 columns in mysql

Employee table is listed below...
Name DOJ DOL
............ ............... ............
Ram 2014-01-12 2014-02-12
Kiran 2014-02-05 2014-07-05
Jhon 2014-01-25 2014-10-01
Expected Output is....
Month Joining_count Leaving_count
........... ................ .................
Jan-2014 2 0
Feb-2014 1 1
. . .
. . .
. . .
I tried with below Mysql Query but i am not able get expected output
please help me
SELECT monthname(current_date) as month,
count( `DATE_OF_JOINING`) as 'Joining_count' ,
count( `DATE_OF_LEAVING`) as 'leaving_count' ,
group by year(`DATE_OF_JOINING`),month('DATE_OF_JOINING`),
year(`DATE_OF_LEAVING),month(`DATE_OF_LEAVING)
You are performing two different aggregations on the same data - so you'd need two aggregate queries joined.
Unfortunately, mysql doesn't have a full outer join, so it's a bit of a hassle to handle both months where someone left but nobody joined and months somebody joined by nobody left. I solves this with three joins - one query to get all the possible dates, and another two for each aggregation, although there are other ways.
SELECT my_table.d, COALESCE(join_count, 0), COALESCE(leave_count, 0)
FROM (SELECT DISTINCT DATE_FORMAT(doj, '%b-%y') AS d
FROM my_table
UNION
SELECT DISTINCT DATE_FORMAT(dol, '%b-%y')
FROM my_table) dates
LEFT JOIN (SELECT DATE_FORMAT(doj, '%b-%y') d, COUNT(*) AS join_count
FROM my_table
GROUP BY DATE_FORMAT(doj, '%b-%y')
) joins ON dates.d = joins.d
LEFT JOIN (SELECT DATE_FORMAT(dol, '%b-%y') d, COUNT(*) AS leave_count
FROM my_table
GROUP BY DATE_FORMAT(dol, '%b-%y')
) leaves ON dates.d = leaves.d
Basically, you need two queries, one for grouping the joining dates and one for the leaving dates. You can combine both results using UNION ALL and treat the combined result as a new table and select (and SUM) the values then.
SELECT
s.month,
SUM(s.Joining_count) AS Joining_count,
SUM(s.Leaving_count) AS Leaving_count
FROM
(
SELECT
DATE_FORMAT(doj, '%b-%y') as `month`,
COUNT(ej.doj) as 'Joining_count',
0 AS 'Leaving_count'
FROM employee ej
GROUP BY `month`
UNION ALL
SELECT
DATE_FORMAT(dol, '%b-%y') as `month`,
0 as 'Joining_count',
COUNT(el.dol) as 'Leaving_count'
FROM employee el
GROUP BY `month`
) s
GROUP BY s.`month`;
Output will be
month Joining_count Leaving_count
---------------------------------------------
Feb-2014 1 1
Jan-2014 2 0
Jul-2014 0 1
Oct-2014 0 1

Mysql Query [0002] [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
So I got two Mysql tables and I'm having a hard time doing following query.
Query: Get the the sales value per month, per city
users
id, name, city
sales
*id, user_id, name, amount, date (datetime)*
I started with SELECT MONTHNAME(datetime) AS month GROUP BY month.
Thanks in advance!
Assuming you want a figure of 0 for any month and / or city which doesn't have any sales, but where there are sales for other cities for that month then something like this:-
Cross join a pair of subselects, one to get a list of the months used and one to get a list of the cities, then join those against the records to get the amounts for that month / city, and sum those amounts up:-
SELECT Sub1.YearMonth, Sub2.city, SUM(sales.amount)
FROM (SELECT DISTINCT EXTRACT(YEAR_MONTH FROM `date`) AS YearMonth
FROM sales) Sub1
CROSS JOIN (SELECT DISTINCT city FROM users) Sub2
LEFT OUTER JOIN sales ON Sub1.YearMonth = EXTRACT(YEAR_MONTH FROM sales.`date`)
LEFT OUTER JOIN users ON sales.user_id = users.id AND Sub2.city = sales.city
GROUP BY Sub1.YearMonth, Sub2.city
ORDER BY Sub1.YearMonth, Sub2.city
Down side of this is that if you have a month where nothing was sold to anybody then this month will not appear at all. To get around this you would need to change the subselect for the months to instead take a start date and add a range of numbers to it to get each month.
An example of generating a range is as follows:-
SELECT Sub1.YearMonth, Sub2.city, SUM(sales.amount)
FROM (SELECT EXTRACT(YEAR_MONTH FROM DATE_ADD('2009-01-01', INTERVAL a.i*100+b.i*10+c.i MONTH)) AS aMonth
FROM (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a,
(SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b,
(SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c
WHERE DATE_ADD('2009-01-01', INTERVAL a.i*100+b.i*10+c.i MONTH) <= '2014-12-01') Sub1
CROSS JOIN (SELECT DISTINCT city FROM users) Sub2
LEFT OUTER JOIN sales ON Sub1.YearMonth = EXTRACT(YEAR_MONTH FROM sales.`date`)
LEFT OUTER JOIN users ON sales.user_id = users.id AND Sub2.city = sales.city
GROUP BY Sub1.YearMonth, Sub2.city
ORDER BY Sub1.YearMonth, Sub2.city
This example is giving every month between 2009-01-01 and 2014-12-01 inclusive, then getting all the sales by city for that range. It will cope with a range of up to 1000 months.
Hey noob (nice name btw) :D
Try this one, I've added the YEAR() on the GROUP BY clause since month numbers are repeating per year, and I suppose you do not want the sum of sales of records with the same month but different in year. You can just omit the year on the SELECT statement if you do not want it.
SELECT YEAR(S.date) AS Year
, MONTHNAME(S.date) AS Month
, U.city
, SUM(S.amount) AS SalesPerMonthPerCity
FROM sales S
INNER JOIN users U ON U.id = S.user_id
GROUP BY YEAR(S.date), MONTH(S.date), U.city
You have an SQL Fiddle test page so you and others can try the solution.
This is the query in question:
SELECT MONTHNAME(`date`) AS Monthly
, `city`
, SUM(`amount`) AS Profit
FROM sales
INNER JOIN users ON userid = idu
GROUP BY MONTHNAME(`date`), `city` WITH ROLLUP
Used the WITH ROLLUP to add a subtotal by month and a grand total at the end.
PS: I agree with the comments, you should show some more effort on solving your problem, you are not new on SO so you should be aware of that ;)

SQL query to bring months that doesn't exist in a column

I want to count the number of rows with respect to the month that they've written in database. My database has a column named created_date. you can see what i've done bellow :
select month(created_date) as "Month", count(created_date) as "Count" from transactions group by month(created_date)
what this query returns is something like this :
{'Month':1,'Count':10}
this happens because i only have one month in my database, while i need to have all months in results, including months that doesn't exist in database, like this :
{'Month':1,'Count':10}
{'Month':2,'Count':0}
{'Month':3,'Count':0}
{'Month':4,'Count':0}
{'Month':5,'Count':0}
{'Month':6,'Count':0}
{'Month':7,'Count':0}
{'Month':8,'Count':0}
{'Month':9,'Count':0}
{'Month':10,'Count':0}
{'Month':11,'Count':0}
{'Month':12,'Count':0}
how should i do it?
You could JOIN to a list of months so you can get a row even for months that don't exist in your table:
SELECT m.month,
COUNT(t.created_date)
FROM (SELECT 1 AS month UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12) m
LEFT JOIN transactions t ON MONTH(t.created_date) = m.month
GROUP BY m.month
SELECT * from
(
SELECT 1 as month UNION ALL
SELECT 2 as month UNION ALL
..
SELECT 12 as month
) as months
left outer join
(
select month(created_date) as "Month", count(created_date) as "Count" from transactions group by month(created_date)
) as data
on (months.month=data.month)
ORDER BY months.month