my problem is in selecting counts of records from database based on year and month of their creatin. I have this MySQL query:
SELECT year, m, month, COUNT(bans.ban_id) AS total
FROM (
SELECT year, m, month
FROM
(SELECT YEAR(CURDATE()) year UNION ALL SELECT YEAR(CURDATE())-1) years,
(
SELECT 1 m, 'Jan' month
UNION ALL SELECT 2 m, 'Feb' month
UNION ALL SELECT 3 m, 'Mar' month
UNION ALL SELECT 4 m, 'Apr' month
UNION ALL SELECT 5 m, 'May' month
UNION ALL SELECT 6 m, 'Jun' month
UNION ALL SELECT 7 m, 'Jul' month
UNION ALL SELECT 8 m, 'Aug' month
UNION ALL SELECT 9 m, 'Sep' month
UNION ALL SELECT 10 m, 'Oct' month
UNION ALL SELECT 11 m, 'Nov' month
UNION ALL SELECT 12 m, 'Dec' month
) months
) ym
LEFT JOIN bans
ON ym.year = YEAR(bans.ban_created_at)
AND ym.m = MONTH(bans.ban_created_at)
WHERE
(year=YEAR(CURDATE()) AND m<=MONTH(CURDATE()))
OR
(year<YEAR(CURDATE()) AND m>MONTH(CURDATE()))
AND
(ban_banlist_id = 13)
GROUP BY year, m
It is working almost properly, but - problem is that the query returns only values with valid count of records - e.g. when there is no record in the month, it is not returned.
The reason is that the condition on the right table (bans) is on the WHERE clause, so it's apply on the result, with the effect that only month with occurences will be selected. Put it on the ON clause of the LEFT JOIN clause, like this:
SELECT year, m, month, COUNT(bans.ban_id) AS total
FROM (
SELECT year, m, month
FROM
(SELECT YEAR(CURDATE()) year UNION ALL SELECT YEAR(CURDATE())-1) years,
(
SELECT 1 m, 'Jan' month
UNION ALL SELECT 2 m, 'Feb' month
UNION ALL SELECT 3 m, 'Mar' month
UNION ALL SELECT 4 m, 'Apr' month
UNION ALL SELECT 5 m, 'May' month
UNION ALL SELECT 6 m, 'Jun' month
UNION ALL SELECT 7 m, 'Jul' month
UNION ALL SELECT 8 m, 'Aug' month
UNION ALL SELECT 9 m, 'Sep' month
UNION ALL SELECT 10 m, 'Oct' month
UNION ALL SELECT 11 m, 'Nov' month
UNION ALL SELECT 12 m, 'Dec' month
) months
) ym
LEFT JOIN bans
ON ym.year = YEAR(bans.ban_created_at)
AND ym.m = MONTH(bans.ban_created_at)
AND ban_banlist_id = 13
WHERE
(year=YEAR(CURDATE()) AND m<=MONTH(CURDATE()))
OR
(year<YEAR(CURDATE()) AND m>MONTH(CURDATE()))
GROUP BY year, m
Related
I have this following which fetches the data Month and Year wise
SELECT
y.year,
m.month,
COALESCE(SUM(o.totalsale), 0) AS totalsale
FROM
(SELECT 2017 AS year UNION SELECT 2018 UNION SELECT 2019) y
CROSS JOIN
(SELECT 1 AS month UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION
SELECT 7 UNION SELECT 8 UNION SELECT UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) m
LEFT JOIN order o
ON y.year = YEAR(FROM_UNIXTIME(o.time)) AND
m.month = MONTH(FROM_UNIXTIME(o.time)) AND
o.time BETWEEN 1561953600 AND 1577854740
WHERE
d.year = 2019 OR -- display all of 2019
(d.year = 2018 AND m.month BETWEEN 6 AND 12) -- and second half 2018
GROUP BY
y.year,
m.month;
I also want to fetch data Week wise within this query.
I have a query that count rows between dates(3 months). It shows me the total rows for each month which have records in that month. But if records in month not exists, the query isn`t showing me the null or zero value.
I've tried left join, cross join, coalesce(t2.id,0) and I'm don't achieve anything.
Here's my code:
SELECT CONCAT(t1.month, ' ' , y.years) as month, COUNT(t2.id) as quantity
FROM
( SELECT 'Jan' month UNION ALL
SELECT 'Feb' month UNION ALL
SELECT 'Mar' month UNION ALL
SELECT 'Apr' month UNION ALL
SELECT 'May' month UNION ALL
SELECT 'Jun' month UNION ALL
SELECT 'Jul' month UNION ALL
SELECT 'Aug' month UNION ALL
SELECT 'Sep' month UNION ALL
SELECT 'Oct' month UNION ALL
SELECT 'Nov' month UNION ALL
SELECT 'Dec' month ) t1 CROSS JOIN
(SELECT DISTINCT YEAR(data) as years FROM negocio) y
LEFT JOIN
(SELECT id, data FROM negocio) t2
on t1.month = LEFT(monthname(t2.data), 3) AND y.years = YEAR(t2.data)
WHERE (t2.data BETWEEN NOW() - INTERVAL 3 MONTH AND NOW())
GROUP BY t1.month, y.years ORDER BY year(STR_TO_DATE(years, '%YYYY')), month(STR_TO_DATE(month, '%M'))
What I need is described in this example:
|--------------| |--------------|
|month|quantity| |month|quantity|
|-----|--------| and it must show -> |-----|--------|
| Feb | 5 | | Feb | 5 |
| Apr | 26 | | Mar | 0 |
|--------------| | Apr | 26 |
|--------------|
Any ideas?
Your WHERE clause is what is causing you not to get any data in your output for March, as it will fail for any row in negocio that has a NULL data value, thus effectively converting the LEFT JOIN into an INNER JOIN and removing rows for any month which has no values in negocio (see the manual). That condition should be moved to the ON condition. You do still need a WHERE clause, but it should be on the date generated from t1 and y. I've assumed your requirement on the 9th of May is to count values from the 9th of February onwards, in which case the required condition is:
WHERE STR_TO_DATE(CONCAT_WS('-', y.year, t1.month, DAY(CURDATE())), '%Y-%b-%d') BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
This query should give you the results you want. For ease of ordering and joining to the negocio table, I've added a month number field to your months (t2) table:
SELECT CONCAT(t1.month, ' ' , y.year) as month,
COUNT(t2.id) as quantity
FROM
(SELECT 1 AS mnum, 'Jan' AS month UNION ALL
SELECT 2, 'Feb' UNION ALL
SELECT 3, 'Mar' UNION ALL
SELECT 4, 'Apr' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'Jun' UNION ALL
SELECT 7, 'Jul' UNION ALL
SELECT 8, 'Aug' UNION ALL
SELECT 9, 'Sep' UNION ALL
SELECT 10, 'Oct' UNION ALL
SELECT 11, 'Nov' UNION ALL
SELECT 12, 'Dec' ) t1 CROSS JOIN
(SELECT DISTINCT YEAR(data) AS year FROM negocio) y
LEFT JOIN negocio t2 ON t1.mnum = MONTH(t2.data) AND y.year = YEAR(t2.data) AND t2.data BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
WHERE STR_TO_DATE(CONCAT_WS('-', y.year, t1.month, DAY(CURDATE())), '%Y-%b-%d') BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
GROUP BY y.year, t1.month, t1.mnum
ORDER BY y.year, t1.mnum
Demo on dbfiddle
I need a monthly distribution of my data by using one single mysql statement. Expected result of the statement below: A table with 12 rows. If in one month no data is available it should return 0 for this month. However in the result of the current SQL Statement one or more rows are missing if data is missing. What I am Missing?
SELECT m.Month as m1, MONTH(FROM_UNIXTIME(crdate)) as m2,
SUM(CASE WHEN keylock = 1 THEN 1 ELSE 0 END) AS c1, SUM(CASE WHEN keylock = 2 THEN 1 ELSE 0 END) AS c2
FROM tx_bhm_domain_model_access a
RIGHT OUTER JOIN (
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 ) m
ON MONTH(FROM_UNIXTIME(a.crdate)) = m.Month
WHERE YEAR(FROM_UNIXTIME(a.crdate))=2018
GROUP BY m.Month
ORDER BY m.Month
The YEAR() comparison needs to go in the ON clause. I much prefer LEFT JOIN:
SELECT m.Month as m1, MONTH(FROM_UNIXTIME(crdate)) as m2,
SUM(CASE WHEN keylock = 1 THEN 1 ELSE 0 END) AS c1,
SUM(CASE WHEN keylock = 2 THEN 1 ELSE 0 END) AS c2
FROM (SELECT 1 AS Month UNION ALL
SELECT 2 AS Month UNION ALL
SELECT 3 AS Month UNION ALL
SELECT 4 AS Month UNION ALL
SELECT 5 AS Month UNION ALL
SELECT 6 AS Month UNION ALL
SELECT 7 AS Month UNION ALL
SELECT 8 AS Month UNION ALL
SELECT 9 AS Month UNION ALL
SELECT 10 AS Month UNION ALL
SELECT 11 AS Month UNION ALL
SELECT 12 AS Month
) m LEFT JOIN
tx_bhm_domain_model_access a
ON MONTH(FROM_UNIXTIME(a.crdate)) = m.Month AND
YEAR(FROM_UNIXTIME(a.crdate)) = 2018
GROUP BY m.Month
ORDER BY m.Month;
I also changed the UNIONs to UNION ALLs. There is no need to incur the overhead of removing duplicates.
I have a query that I am using to pull back the total costs per months for the previous 6 months of data. The issue I need to solve is when there is no records for a specific month, nothing is returned and only 5 months are shown.
I need to modify this query to always show the 6 months, even when there is no data for a specific month but I am unsure how to accomplish this.
select sum(cost),
CASE
WHEN MONTH(collection_date) = 1 THEN 'January'
WHEN MONTH(collection_date) = 2 THEN 'February'
WHEN MONTH(collection_date) = 3 THEN 'March'
WHEN MONTH(collection_date) = 4 THEN 'April'
WHEN MONTH(collection_date) = 5 THEN 'May'
WHEN MONTH(collection_date) = 6 THEN 'June'
WHEN MONTH(collection_date) = 7 THEN 'July'
WHEN MONTH(collection_date) = 8 THEN 'August'
WHEN MONTH(collection_date) = 9 THEN 'September'
WHEN MONTH(collection_date) = 10 THEN 'October'
WHEN MONTH(collection_date) = 11 THEN 'November'
WHEN MONTH(collection_date) = 12 THEN 'December'
ELSE 'NULL'
END AS datemodified
from invoices
WHERE collection_date >= DATE_SUB(now(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date asc;
Sample of the results with an empty month
COST Datemodified
300 September
200 November
200 December
Desired output
COST Datemodified
0 August
300 September
0 October
200 November
200 December
You can create fake month data and join your invoices table to it. Try this:
SELECT SUM(cost), months.name AS datemodified
FROM (SELECT 1 AS num, 'January' AS name
UNION SELECT 2, 'February'
UNION SELECT 3, 'March'
UNION SELECT 4, 'April'
UNION SELECT 5, 'May'
UNION SELECT 6, 'June'
UNION SELECT 7, 'July'
UNION SELECT 8, 'August'
UNION SELECT 9, 'September'
UNION SELECT 10, 'October'
UNION SELECT 11, 'November'
UNION SELECT 12, 'December') months
LEFT JOIN invoices.collection_date = months.num
WHERE collection_date >= DATE_SUB(NOW(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date ASC;
However, that gives you all the 12 months. To get only the 6 last months, you need to dynamically generate your fake month data:
SELECT SUM(cost),
CASE num WHEN 1 THEN 'January'
WHEN 2 THEN 'February'
WHEN 3 THEN 'March'
WHEN 4 THEN 'April'
WHEN 5 THEN 'May'
WHEN 6 THEN 'June'
WHEN 7 THEN 'July'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'October'
WHEN 11 THEN 'November'
WHEN 12 THEN 'December'
END AS datemodified
FROM (SELECT MONTH(NOW()) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 2 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 3 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 4 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 5 MONTH)) AS num) months
LEFT JOIN invoices.collection_date = months.num
WHERE collection_date >= DATE_SUB(NOW(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date ASC;
I want to get following result for given year
month | sales
jan 0.00
feb 4.00
mar 0.00
apr 45.00
.
.
.
.
dec 0.00
table that i have is "sales" and it has salse_value, sales_date as columns.
question is how can get above result using case when statement in MYSQL
you need to generate all month name and join it with your tables name so months that doesn't have records will have zero value for total sales
SELECT a.MonthName, COALESCE(SUM(b.sales_value), 0) totalSales
FROM
(
SELECT 'Jan' AS monthName, 1 monthOrder
UNION
SELECT 'Feb' AS monthName, 2 monthOrder
UNION
SELECT 'Mar' AS monthName, 3 monthOrder
UNION
SELECT 'Apr' AS monthName, 4 monthOrder
UNION
SELECT 'May' AS monthName, 5 monthOrder
UNION
SELECT 'Jun' AS monthName, 6 monthOrder
UNION
SELECT 'Jul' AS monthName, 7 monthOrder
UNION
SELECT 'Aug' AS monthName, 8 monthOrder
UNION
SELECT 'Sep' AS monthName, 9 monthOrder
UNION
SELECT 'Oct' AS monthName, 10 monthOrder
UNION
SELECT 'Nov' AS monthName, 11 monthOrder
UNION
SELECT 'Dec' AS monthName, 12 monthOrder
) a LEFT JOIN sales b
ON a.monthName = b.monthName
GROUP BY a.monthName
ORDER BY a.MonthOrder ASC
SELECT MONTHNAME(date_column), SUM(sales) sales
FROM TableName
GROUP BY MONTHNAME(date_column);
If you want to get abbreviated names for month, you can try
SELECT DATE_FORMAT(sales_date, '%b') AS month, SUM(sales_value)
FROM sales
GROUP BY month;
If there is no record of a particular month result won't contain any row for that month.