get average value depending on month? - mysql

My table:
rating date
4 12/02/2013
3 12/02/2013
2.5 12/01/2013
3 12/01/2013
4.5 21/11/2012
5 10/11/2012
If I give input as 3 the last three months (02,01,12), average of rating result should come
I tried by using GROUP BY but I get this result:
rating month
3.5 02
2.75 01
For the 12th month no rating so no output.....
My desired result:
rating month
3.5 02
2.75 01
0 12

The problem is that you want to return months that do not exist. If you do not have a calendar table with dates, then you will want to use something like the following:
select d.mth Month,
coalesce(avg(t.rating), 0) Rating
from
(
select 1 mth union all
select 2 mth union all
select 3 mth union all
select 4 mth union all
select 5 mth union all
select 6 mth union all
select 7 mth union all
select 8 mth union all
select 9 mth union all
select 10 mth union all
select 11 mth union all
select 12 mth
) d
left join yourtable t
on d.mth = month(t.date)
where d.mth in (1, 2, 12)
group by d.mth
See SQL Fiddle with Demo

SELECT coalesce(avg(rating), 0.0) avg_rating, req_month
FROM yourTable
RIGHT JOIN
(SELECT month(now()) AS req_month
UNION
SELECT month(now() - INTERVAL 1 MONTH) AS req_month
UNION
SELECT month(now() - INTERVAL 2 MONTH) AS req_month) tmpView
ON month(yourTable.date) = tmpView.req_month
WHERE yourTable.date > ( (curdate() - INTERVAL day(curdate()) - 1 DAY) - INTERVAL 2 MONTH)
OR ratings.datetime IS NULL
GROUP BY month(yourTable.date);

Related

Select zero when no results in date range

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;

MySQL: How to search record for every month between two dates in mysql, return 0 if null ? with group by date clause [duplicate]

I have a table with sell orders and I want to list the COUNT of sell orders per day, between two dates, without leaving date gaps.
This is what I have currently:
SELECT COUNT(*) as Norders, DATE_FORMAT(date, "%M %e") as sdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;
The result I'm getting is as follows:
6 May 1
14 May 4
1 May 5
8 Jun 2
5 Jun 15
But what I'd like to get is:
6 May 1
0 May 2
0 May 3
14 May 4
1 May 5
0 May 6
0 May 7
0 May 8
.....
0 Jun 1
8 Jun 2
.....
5 Jun 15
Is that possible?
Creating a range of dates on the fly and joining that against you orders table:-
SELECT sub1.sdate, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY), "%M %e") as sdate
FROM (SELECT 0 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)units
CROSS JOIN (SELECT 0 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)tens
CROSS JOIN (SELECT 0 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)hundreds
WHERE DATE_SUB(NOW(), INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) BETWEEN DATE_SUB(NOW(), INTERVAL 1 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%M %e")
GROUP BY sub1.sdate
This copes with date ranges of up to 1000 days.
Note that it could be made more efficient easily depending on the type of field you are using for your dates.
EDIT - as requested, to get the count of orders per month:-
SELECT aMonth, COUNT(ORDERS.id) as Norders
FROM
(
SELECT DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%Y%m") as sdate, DATE_FORMAT(DATE_SUB(NOW(), INTERVAL months.i MONTH), "%M") as aMonth
FROM (SELECT 0 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 UNION SELECT 10 UNION SELECT 11)months
WHERE DATE_SUB(NOW(), INTERVAL months.i MONTH) BETWEEN DATE_SUB(NOW(), INTERVAL 12 MONTH) AND NOW()
) sub1
LEFT OUTER JOIN ORDERS
ON sub1.sdate = DATE_FORMAT(ORDERS.date, "%Y%m")
GROUP BY aMonth
You are going to need to generate a virtual (or physical) table, containing every date in the range.
That can be done as follows, using a sequence table.
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
Then, you join this virtual table to your query, as follows.
SELECT IFNULL(orders.Norders,0) AS Norders, /* show zero instead of null*/
DATE_FORMAT(alldates.orderdate, "%M %e") as sdate
FROM (
SELECT mintime + INTERVAL seq.seq DAY AS orderdate
FROM (
SELECT CURDATE() - INTERVAL 1 MONTH AS mintime,
CURDATE() AS maxtime
FROM obs
) AS minmax
JOIN seq_0_to_999999 AS seq
ON seq.seq < TIMESTAMPDIFF(DAY,mintime,maxtime)
) AS alldates
LEFT JOIN (
SELECT COUNT(*) as Norders, DATE(date) AS orderdate
FROM ORDERS
WHERE date <= NOW()
AND date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
) AS orders ON alldates.orderdate = orders.orderdate
ORDER BY alldates.orderdate ASC
Notice that you need the LEFT JOIN so the rows in your output result set will be preserved even if there's no data in your ORDERS table.
Where do you get this sequence table seq_0_to_999999? You can make it like this.
DROP TABLE IF EXISTS seq_0_to_9;
CREATE TABLE seq_0_to_9 AS
SELECT 0 AS seq 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;
DROP VIEW IF EXISTS seq_0_to_999;
CREATE VIEW seq_0_to_999 AS (
SELECT (a.seq + 10 * (b.seq + 10 * c.seq)) AS seq
FROM seq_0_to_9 a
JOIN seq_0_to_9 b
JOIN seq_0_to_9 c
);
DROP VIEW IF EXISTS seq_0_to_999999;
CREATE VIEW seq_0_to_999999 AS (
SELECT (a.seq + (1000 * b.seq)) AS seq
FROM seq_0_to_999 a
JOIN seq_0_to_999 b
);
You can find an explanation of all this in more detail at http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/
If you're using MariaDB version 10+, these sequence tables are built in.
First create a Calendar Table
SELECT coalesce(COUNT(O.*),0) as Norders, DATE_FORMAT(C.date, "%M %e") as sdate
FROM Calendar C
LEFT JOIN ORDERS O ON C.date=O.date
WHERE O.date <= NOW() AND O.date >= NOW() - INTERVAL 1 MONTH
GROUP BY DAY(date)
ORDER BY date ASC;

How to get the all mondays in a month?

I want to get all the Mondays in the month of MAY 2015
(using mysql query)
OUTPUT:
MON
04
11
18
25
select row+1 as Mon from
( SELECT #row := #row + 1 as row FROM
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6) t1,
(select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6) t2,
(SELECT #row:=-1) t3 limit 31 ) b where
DATE_ADD('2015-05-01', INTERVAL ROW DAY) between '2015-05-01' and '2015-05-31' and DAYOFWEEK(DATE_ADD('2015-05-01', INTERVAL ROW DAY))=2;
Output
+------------+
| Mon |
+------------+
| 4 |
| 11 |
| 18 |
| 25 |
+------------+
Tweaking a bit this query
For reference, here's another solution - note that the last entry may be null, can be changed to another value if necessary, or wrap in a sub-select and filter on not null.
SET #date='2015-05-01';
SET #offset=7 - WeekDay(#date);
SELECT DAY(DATE_ADD(#date,INTERVAL #offset DAY)) AS 'MON'
UNION SELECT DAY(DATE_ADD(#date,INTERVAL #offset+7 DAY))
UNION SELECT DAY(DATE_ADD(#date,INTERVAL #offset+14 DAY))
UNION SELECT DAY(DATE_ADD(#date,INTERVAL #offset+21 DAY))
UNION DISTINCT SELECT IF(DAY(DATE_ADD(#date,INTERVAL #offset+28 DAY))>21,
DAY(DATE_ADD(#date,INTERVAL #offset+28 DAY)),
DAY(DATE_ADD(#date,INTERVAL #offset+21 DAY)))
;
SQL Fiddle: http://sqlfiddle.com/#!9/fa4ce/4
This query returns the two digit day value of the Mondays in a month.
This requires the "month" as a date of the first day of the month, as a value in the SELECT list of the first inline view (d0). (This inline view query could be tweaked to handle any date value within a month as the specification for a month.)
SELECT DATE_FORMAT(d0.dt + INTERVAL d1.i*6+d2.i DAY,'%d') AS dd
-- , d0.dt + INTERVAL d1.i*6+d2.i DAY AS dt
FROM ( SELECT '2015-05-01' + INTERVAL 0 DAY AS dt
) d0
CROSS
JOIN ( SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
) d1
CROSS
JOIN ( SELECT 0 AS i UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
) d2
WHERE d0.dt + INTERVAL d1.i*6+d2.i DAY < d0.dt + INTERVAL 1 MONTH
AND NOT WEEKDAY(d0.dt + INTERVAL d1.i*6+d2.i DAY)
ORDER BY 1
NOTE: This requires the month to be specified only once, in the first inline view (d0). Everything else is handled in expression that reference this one value.
The WEEKDAY function returns 0 for a date value that is a Monday, so a NOT on the return from the WEEKDAY function will return TRUE for a Monday.
For a supplied date value of '2015-05-01', this returns:
dd
--
04
11
18
25

How to get the average rating for on before some day and after some days in mysql?

I need to get the average rating for a video, for this video there is the field called release date, so i need to get rating for the on before release date and after release date,
now i have the query to get only the rating on before video release date as follows
select d.mth Month,
coalesce(avg(t.rating), 0) Rating
from
(
select 1 mth union all
select 2 mth union all
select 3 mth union all
select 4 mth union all
select 5 mth union all
select 6 mth union all
select 7 mth union all
select 8 mth union all
select 9 mth union all
select 10 mth union all
select 11 mth union all
select 12 mth
) d
left join wp_fatalcut_videos_rating t
on d.mth = month(t.ratingdate)
where d.mth in (1,2,3,4,5,6,7,8,9,10,11,12) and videoid='3192763' and ratingdate <= '2013-10-09 00:00:00'
group by d.mth
i need to get the rating for the after video release that is after '2013-10-09 00:00:00' in same Query, is this is possible ??
Can you just do this? If I understand you correct:
select d.mth Month,
coalesce(avg(t.rating), 0) Rating,
coalesce(avg(tafter.rating), 0) RatingAfter
from
(
select 1 mth union all
select 2 mth union all
select 3 mth union all
select 4 mth union all
select 5 mth union all
select 6 mth union all
select 7 mth union all
select 8 mth union all
select 9 mth union all
select 10 mth union all
select 11 mth union all
select 12 mth
) d
left join wp_fatalcut_videos_rating t
on d.mth = month(t.ratingdate)
AND t.videoid='3192763' and t.ratingdate <= '2013-10-09 00:00:00'
left join wp_fatalcut_videos_rating tafter
on d.mth = month(tafter.ratingdate)
AND tafter.videoid='3192763' and tafter.ratingdate >= '2013-10-09 00:00:00'
where d.mth in (1,2,3,4,5,6,7,8,9,10,11,12)
group by d.mth
I think that this were statement is unnecessary
where d.mth in (1,2,3,4,5,6,7,8,9,10,11,12)
Becuse you will not have month in the table mth which is for example 0,13, or 14. Right?

append dummy data in fetch result

SELECT COUNT(*),Date(createDate) FROM EEC_Order WHERE createDate
BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY ) AND CURDATE()
group by Date(createDate) ;
COUNT(*) Date(createDate)
3 2013-09-08
1 2013-09-11
2 2013-09-12
Above query is fetching from table that means previous 7 days from current date(assume 2013-09-13) based on group by createdate. Results are showing 3 rows. While fetching time ,I want to add date and count in above RESULT only (not in DB) dummy date which are not there like following. Please help......How to write query for this...
COUNT(*) Date(createDate)
0 2013-09-06
0 2013-09-07
3 2013-09-08
0 2013-09-09
0 2013-09-10
1 2013-09-11
2 2013-09-12
Regards
sakir
esquareinfo
You need a list of the dates and a left outer join. Here is one way to formulate the query:
SELECT COUNT(o.createDate), dates.thedate
FROM (select DATE_SUB(CURDATE(), INTERVAL 7 DAY ) as thedate union all
select DATE_SUB(CURDATE(), INTERVAL 6 DAY ) as thedate union all
select DATE_SUB(CURDATE(), INTERVAL 5 DAY ) as thedate union all
select DATE_SUB(CURDATE(), INTERVAL 4 DAY ) as thedate union all
select DATE_SUB(CURDATE(), INTERVAL 3 DAY ) as thedate union all
select DATE_SUB(CURDATE(), INTERVAL 2 DAY ) as thedate union all
select DATE_SUB(CURDATE(), INTERVAL 1 DAY ) as thedate union all
select DATE_SUB(CURDATE(), INTERVAL 0 DAY ) as thedate
) dates left outer join
EEC_Order o
on Date(createDate) = dates.thedate
group by dates.thedate ;
EDIT:
Doing this dynamically in MySQL isn't so easy, unless you have a numbers or dates table somewhere. The following takes this approach, by first creating a list of 1000 numbers (with a where clause) and then doing the join and aggregation:
select DATE_SUB(CURDATE(), INTERVAL n.n DAY ), count(e.CreateDate)
from (select (n1*100 + n2*10 + n3) as n
from (select 0 as n 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
) n1 cross join
(select 0 as n 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
) n2 cross join
(select 0 as n 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
) n3
where (n1*100 + n2*10 + n3) <= 30
) n left outer join
EEC_Order o
on Date(createDate) = DATE_SUB(CURDATE(), INTERVAL n.n DAY )
group by DATE_SUB(CURDATE(), INTERVAL n.n DAY );