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 ;)
Related
This question already has answers here:
MySQL how to fill missing dates in range?
(6 answers)
Closed last year.
It was hard to find a good title for my question.
I have 3 tables: materials, orders and order_contents.
There are 5 different types of materials in the materials table
The orders table contains the dates for the orders. Orders currently span over 4 months.
The orders are filled with materials in the table called order_contents.
I am trying to get the overall cost per month for materials and display them in a highchart.
Here's the query I run:
SELECT m.name, CONCAT(MONTH(o.order_date), '/', YEAR(o.order_date)) as `month`, SUM(oc.weight * m.price) AS cost
FROM order_contents oc
INNER JOIN orders o ON oc.order_id = o.id
INNER JOIN materials m ON oc.material_id = m.id
GROUP BY MONTH(o.order_date), m.id
ORDER BY m.name, order_date ASC
Here are the results:
The problem is that if a material isn't used in a particular month, it won't generate a record for it (obviously). So when I loop through the results and try to form the hightable data series, it won't fill a month with zero. For example, the material Big Bag is only consumed in January 2022, but since it's the only entry in the data series, it maps with the first month, which is August. I can add logic to fix this problem but I thought I'd ask here first if there is a way to reformat this query to yield the results I'm looking for.
Here's what I'd like to get:
I'm way out of my league here on SQL capabilities for this sort of problem.
Here is a (probably wired) idea:
For a SQL table:
create table temp
(
month int,
year int,
name varchar(16), -- something like material-type
count int,
)
We could run:
select const_year.year, const_month.month, const_name.name, ifnull(count, 0)
from (select 1 month 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) const_month -- now we have a list contains 12 months
left join (select 2020 year union
select 2021 union
select 2022 ) const_year
on true -- now we have a table contains all months between those years
left join (select distinct temp.name as name
from temp) const_name
on true -- join with all distinct names/types
left join (select temp.name as name, temp.year as year, temp.month as month, sum(count) as count
from temp
group by temp.year, temp.month, temp.name -- here is the real query for statistic
) statistic
on statistic.year = const_year.year
and statistic.month = const_month.month
and statistic.name = const_name.name
order by name, year, month -- order results if we need
I think there definitely have more better solution than this. Though it's working for some case.
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.
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;
I have to do a SQL query for getting the incomes of a company on all the months of the year, but some months dont have records in the table.
I have used this query:
SELECT COUNT(wp_dgl_stats.datetime_stamp)*wp_dgl_ads.price as incomes, MONTHNAME(wp_dgl_stats.datetime_stamp) as month
FROM wp_dgl_ads
INNER JOIN wp_dgl_stats ON wp_dgl_stats.id_ad = wp_dgl_ads.id
WHERE YEAR(wp_dgl_stats.datetime_stamp) = 2015
GROUP BY MONTHNAME(wp_dgl_stats.datetime_stamp)
I have to say that wp_dgl_stats contains a record for every click made by an user in certain spaces of the web (the ads showed) with a reference to the ad and a datetime stamp.
This query returns exactly months with incomes and the exact amount. But I need to get also the rest of the months with a 0.
How could this be done?
After a lot of tests I got a proper solution. I will post it here if someone needs for it with an explanation.
SELECT meses.month, CAST(COUNT(stats.id)*ads.precio AS UNSIGNED) as precio
FROM
(
SELECT 1 AS MONTH
UNION SELECT 2 AS MONTH
UNION SELECT 3 AS MONTH
UNION SELECT 4 AS MONTH
UNION SELECT 5 AS MONTH
UNION SELECT 6 AS MONTH
UNION SELECT 7 AS MONTH
UNION SELECT 8 AS MONTH
UNION SELECT 9 AS MONTH
UNION SELECT 10 AS MONTH
UNION SELECT 11 AS MONTH
UNION SELECT 12 AS MONTH
) as meses
LEFT JOIN wp_dgl_stats stats ON meses.month = MONTH(stats.datetime_stamp)
LEFT JOIN wp_dgl_ads ads ON stats.id_ad = ads.id AND YEAR(stats.datetime_stamp) = '2015'
GROUP BY meses.month
Because I am a spanish developer and I need to have spanish month names I selected the month number and with an PHP array we can convert the month number to his spanish name.
If someone have some question, do it, I will be glad to answer.
You could LEFT/RIGHT join on the months:
WITH monthnames AS (SELECT <something that returns a list of the month names> as monthname)
SELECT COALESCE(COUNT(wp_dgl_stats.datetime_stamp)*wp_dgl_ads.price, 0) as incomes, MONTHNAME(wp_dgl_stats.datetime_stamp) as month
FROM wp_dgl_ads
INNER JOIN wp_dgl_stats ON wp_dgl_stats.id_ad = wp_dgl_ads.id
RIGHT JOIN monthnames ON month = monthname
WHERE YEAR(wp_dgl_stats.datetime_stamp) = 2015
GROUP BY MONTHNAME(wp_dgl_stats.datetime_stamp)
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