Get monthly distribution of data within single SELECT statement - mysql

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.

Related

SQL query for last 365 days report

I have a reports table with the following structure :
I want a SQL Query to get the report for the last 365 days by following conditions :
Group dates if the same date is repeated.
The days which the report is not available for the last 365 days, I need those days added to the result rows with 0 as their success and failed recipients.
I tried to get it by group by report dates
SELECT report_date, SUM(success_recipient) as success_recipient, SUM(failed_recipient) as failed_recipient FROM reports GROUP BY report_date;
and I have got the grouped result which satisfies the first condition
Now I need to append the rest of the days in the last 365 days to this result in which 0 as their success and failure recipients.
Expected result :
and so on ..
MYSQL VERSION : 5.6
One way to achieve this is using "with recursive" to generate all dates you need in you output and then outer join to the rest of your query. Note: I use the number 356 as it is in your description but it seems more appropriate to use date difference as this approach does not take into account leap years. Using the query below you will get NULL values in case you have no data. If you need the value 0 you can use coalesce(sum(...), 0).
with recursive
dates as (
select curdate()-356 dt
union all
select dt+1 from dates
where dt < curdate()
)
select
dt report_date,
sum(success_recipient) success_recipient,
sum(failed_recipient) failed_recipient
from dates
left join reports on report_date = dt
group by report_date;
From the above comments and the answer, I could write this query which gave me the expected outcome :
SELECT a.date, SUM(COALESCE(r.success_recipient, 0)), SUM(COALESCE(r.failed_recipient, 0))
FROM (
SELECT curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a) + (1000 * d.a) ) DAY AS date
FROM (select 0 as a union all select 1 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) as a
cross join (select 0 as a union all select 1 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) as b
cross join (select 0 as a union all select 1 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) as c
cross join (select 0 as a union all select 1 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) as d
) a
LEFT JOIN reports r ON a.date = r.report_date
WHERE a.date between DATE_SUB(CURDATE(), INTERVAL 1 YEAR) and now()
GROUP BY a.date;

How to fetch data week wise along side month and year in single query

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.

How to select count different values by different conditions

I have issues to count data by different criteria and to display them on a chart. In the database, I have millions of entries when I try to count the total, the completed, the rejected and expired in daily, weekly and monthly bases, it doesn't show the correct answer. I have 189 on 2019-09-07 but on daily it shows 0 or the current date 2019-09-08 must be with 0 doesn't appear or a different value which doesn't match with the actual value selected directly by the workbench.
SELECT
D.dates AS dates,
IFNULL(V.total,0) AS total,
IFNULL(V.completed,0) AS complete,
IFNULL(V.rejected,0) AS rejected,
IFNULL(EX.e,0) AS expired
FROM (
SELECT *
FROM (
SELECT CURDATE() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY AS dates
FROM (
SELECT 0 AS a UNION ALL SELECT 1 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
) AS a
CROSS JOIN (
SELECT 0 AS a UNION ALL SELECT 1 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
) AS b
CROSS JOIN (
SELECT 0 AS a UNION ALL SELECT 1 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
) AS c
) AS generator
WHERE generator.dates BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
) AS D
LEFT JOIN (
SELECT
DATE(live_date) AS live_date,
COUNT(*) as total,
SUM(CASE WHEN status = 'complete' THEN 1 ELSE 0 END) AS completed,
SUM(CASE WHEN status = 'rejected' THEN 1 ELSE 0 END) AS rejected
FROM agreement
WHERE DATE(live_date) BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
GROUP BY DATE(live_date)
ORDER BY DATE(live_date) DESC
) AS V ON D.dates = V.live_date
LEFT JOIN (
SELECT DATE(expire_date) AS dEx, COUNT(*) AS e
FROM agreement
WHERE status = 'open'
AND DATE(expire_date) BETWEEN CURDATE() - INTERVAL 90 DAY AND CURDATE()
GROUP BY dEx
ORDER BY DATE(expire_date) DESC
) AS EX ON D.dates = EX.dEx;
Expecting to have the correct value for total, completed, rejected and expired in single SELECT to avoid high complexity and time to load the data instead of multiple SELECT with JOIN.

Getting 0 in GroupByMonth when it returns NULL

I`m trying to get values with mysql, but it gives no 0 for specific months when there is no value for it...
I tried IFNULL but it makes same and not returning 0 for the empty month.
SELECT IFNULL(COUNT(`bileti_id`),0) FROM `wp_biletistatus` WHERE
`user_id`= 1 and `status`=1 GROUP BY MONTH(`date`) ORDER BY `date` ASC
Limit 10
Here is my query as well for months which i want to get:
SELECT MONTHNAME(`date`) FROM `wp_biletistatus` WHERE `user_id`= 1
GROUP BY MONTH(`date`) ORDER BY `date` ASC Limit 10
Can someone help me how to get 0 for the months which has no value to count.
the reason why you are not getting zero on months that don't have value is because simply it does not exist. You need to have a fix values or atleast a subquery which have the list of all months, example.
SELECT months.MonthNum,
COUNT(a.bileti_id)
FROM (
SELECT 1 AS MonthNum 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
) months
LEFT JOIN wp_biletistatus a
ON months.MonthNum = MONTH(a.date)
WHERE a.user_id= 1 and a.status=1
GROUP BY months.MonthNum
ORDER BY months.MonthNum

MySql Group by Month

I have set of dates and it's corresponding sales and I want to go group my month with order by month.
Not it may be possible that entry of few months can not be present and if so then for corresponding months value will be 0 else what ever sum available for other month.
something like this would maybe do a trick: - but you need to take care of years, this is only month based
SELECT s1.m as mnth,IFNULL(s2.smv,0) as smv FROM
(SELECT 1 as m 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) s1
LEFT JOIN
(SELECT MONTH(startdatum) as mnth,
SUM(somevalue) as smv
FROM db_table GROUP BY mnth) s2
ON s1.m=s2.mnth
YOU can try:
//this will give you all entries for the required month
SELECT * FROM tbl1 WHERE date=MONTH('datecolumn_name') ORDER BY MONTH(datecolumn_name) ASC;
juz run thiz query u get idea :
(SELECT #d:=date_add(#d,interval 1 MONTH) AS month_view,date_format(#c,'%b-%Y') AS month_name,#a:= #c AS month_start,#b:=if(last_day(#a)>'2014-02-24','2014-02-24', last_day(#a)) AS month_end, #c:= CASE WHEN date_add(#b,interval 1 DAY)>'2014-02-24' THEN NULL ELSE date_add(#b , INTERVAL 1 DAY) END AS dummy_val FROM table_name,
(SELECT #c:='2013-01-01', #d:='2013-01-01') AS month_temp WHERE #c IS NOT NULL)
'table_name' give any table name from ur database
from_date=2013-01-01
to_date=2014-02-24