I have two tables of users and articles and I want to count how many new users and how many new articles I have in the past 7 days.
tbl_users:
[Code, Username, Createdate]
1,David,01/01/2022
2,Henry,02/01/2022
tbl_articles:
[Code, Header, Createdate]
1,Hello,01/01/2022
2,Goodbye,02/01/2022
This query works now but it's slow and long. Please help me fix this query (I know it's bad) and if it's possible to add diff columns for both counters:
(Please go easy on me with the comments)
select articles.days_back,articles.count, users.count as users from (
select 0 as days_back,count(*) as count from tbl_articles where date(createdate)< date_add(curdate(), interval -0 day)
union all
select 1,count(*) from tbl_articles where date(createdate)< date_add(curdate(), interval -1 day)
union all
select 2,count(*) from tbl_articles where date(createdate)< date_add(curdate(), interval -2 day)
union all
select 3,count(*) from tbl_articles where date(createdate)< date_add(curdate(), interval -3 day)
union all
select 4,count(*) from tbl_articles where date(createdate)< date_add(curdate(), interval -4 day)
union all
select 5,count(*) from tbl_articles where date(createdate)< date_add(curdate(), interval -5 day)
union all
select 6,count(*) from tbl_articles where date(createdate)< date_add(curdate(), interval -6 day)
union all
select 7,count(*) from tbl_articles where date(createdate)< date_add(curdate(), interval -7 day)
) as articles
left join
(
select 0 as days_back,count(*) as count from tbl_users where date(createdate)< date_add(curdate(), interval -0 day)
union all
select 1,count(*) from tbl_users where date(createdate)< date_add(curdate(), interval -1 day)
union all
select 2,count(*) from tbl_users where date(createdate)< date_add(curdate(), interval -2 day)
union all
select 3,count(*) from tbl_users where date(createdate)< date_add(curdate(), interval -3 day)
union all
select 4,count(*) from tbl_users where date(createdate)< date_add(curdate(), interval -4 day)
union all
select 5,count(*) from tbl_users where date(createdate)< date_add(curdate(), interval -5 day)
union all
select 6,count(*) from tbl_users where date(createdate)< date_add(curdate(), interval -6 day)
union all
select 7,count(*) from tbl_users where date(createdate)< date_add(curdate(), interval -7 day)
) as users
on articles.days_back=users.days_back
Something like that?
SELECT Counter.Articles, Counter.Users (
SELECT COUNT(1) FROM tbl_articles WHERE DATE(createdate) BETWEEN (CURRENT_TIMESTAMP() AND DATE_ADD(CURRENT_TIMESTAMP(), INTERVAL -7 DAY)) Articles,
SELECT COUNT(1) FROM tbl_users WHERE DATE(createdate) BETWEEN (CURRENT_TIMESTAMP() AND DATE_ADD(CURRENT_TIMESTAMP(), INTERVAL -7 DAY)) Users
) Counter;
haven't tested it btw
It is not as easy as just use a group by, but as you thought group by is important to do this. I guess there will be no simple 5 line query to get your expected result.
I would suggest to do multiple queries and some backend code instead of a single query. It is hard to read, understand and maintain.
But it is possible.
A GROUP BY DATE_FORMAT(Created,'%Y-%m-%d') in a simple select would already give you the count per day.
The next problem to solve is, that you want the days_back instead of the date itself. Thats also quite simple with DATEDIFF(DATE_FORMAT(NOW(),'%Y-%m-%d'), DATE_FORMAT(Createdate,'%Y-%m-%d')).
But the third requirement is the difficult. You don't want the amount per day but the sum up to that day, for a variable amount of days back. Unfortunatly, mysql does not offer a Sequence (like SELECT numbers from 1 to X) to join with the other parts of the query. If you can live with a restriction to somewhat about 300 years, https://stackoverflow.com/a/9296238/4675841 will help there.
To get article counts and user counts together, you coult either use union (all) or a join, I used the join and came up with this query.
SELECT user_query.diff as days_back, article_count as count, user_count as users
FROM (
SELECT DATEDIFF(DATE_FORMAT(NOW(),'%Y-%m-%d'), gen_date) as diff, COUNT(create_date) as user_count
FROM (
SELECT adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date
FROM
(SELECT 0 t0 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) t0,
(SELECT 0 t1 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) t1,
(SELECT 0 t2 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) t2,
(SELECT 0 t3 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) t3,
(SELECT 0 t4 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) t4) possible_dates
LEFT JOIN (SELECT DATE_FORMAT(Createdate,'%Y-%m-%d') as create_date FROM tbl_users) counts
ON create_date <= gen_date
WHERE gen_date BETWEEN DATE_SUB(NOW(), INTERVAL 20 DAY) AND NOW()
GROUP BY gen_date) user_query
INNER JOIN (
SELECT DATEDIFF(DATE_FORMAT(NOW(),'%Y-%m-%d'), gen_date) as diff, COUNT(create_date) as article_count
FROM (
SELECT adddate('1970-01-01',t4*10000 + t3*1000 + t2*100 + t1*10 + t0) gen_date
FROM
(SELECT 0 t0 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) t0,
(SELECT 0 t1 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) t1,
(SELECT 0 t2 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) t2,
(SELECT 0 t3 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) t3,
(SELECT 0 t4 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) t4) v
LEFT JOIN (SELECT DATE_FORMAT(Createdate,'%Y-%m-%d') as create_date FROM tbl_articles) counts
ON create_date <= gen_date
WHERE gen_date BETWEEN DATE_SUB(NOW(), INTERVAL 20 DAY) AND NOW()
GROUP BY gen_date) article_query
ON user_query.diff = article_query.diff
ORDER BY days_back
Try it out.
Related
I'm trying to generate seven rows for each date in the last seven days, and join with a query from transactions table. The aim is to have a table with each date, and the cumulative total of the quantity column in transactions from the first entry up to the date:
| date | stockOnDate |
|---------------|---------------|
| 2021-10-15 | 10 |
| 2021-10-16 | 3 |
| 2021-10-17 | 0 |
| 2021-10-18 | 9 |
| 2021-10-19 | 15 |
| 2021-10-20 | 15 |
| 2021-10-21 | 15 |
I can get the list of dates, and can join, but can't filter the nested queries:
SELECT v.*, t.*
FROM ( SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 7 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date`
FROM (SELECT 0 t0 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) t0,
(SELECT 0 t1 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) t1,
(SELECT 0 t2 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) t2,
(SELECT 0 t3 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) t3 ) AS v
LEFT JOIN (SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate
FROM `transactions`
WHERE tDate <= v.`date`) AS t ON t.tDate = v.`date`
WHERE v.`date` >= DATE_SUB(NOW(),INTERVAL 7 DAY) AND v.`date` <= DATE(NOW())
But I'm receviing the following error:
#1054 - Unknown column 'tDate' in 'where clause'
If I replace WHERE tDate <= v.date with WHERE DATE(timestamp) <= v.date I get the same error for v.date - I can't seem to access the value of the parent tables.
I'm not great with MySQL but can't seem to find a solution, where am I going wrong?
Solution by ProGuru
Thanks to ProGuru's answer below, the below query works as expected (using <= instead of = in the JOIN was key)
SELECT b.date, SUM(a.quantity) AS stockOnDate
FROM (
SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 6 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date`
FROM (SELECT 0 t0 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) t0,
(SELECT 0 t1 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) t1,
(SELECT 0 t2 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) t2,
(SELECT 0 t3 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) t3
) b
LEFT JOIN transactions a ON DATE(a.timestamp) <= b.date
WHERE b.date BETWEEN DATE(NOW()) - INTERVAL 6 DAY AND DATE(NOW())
AND a.organisationId = 1
GROUP BY b.date
ORDER BY b.date ASC
I can also change the GROUP BY to GROUP BY a.itemID, b.date to get the stock level on the given date for each itemId.
To get the cumulative sum of quantity, the date join needs to use <=
Revised SQL
SELECT b.date, SUM(COALESCE(a.quantity, 0))
FROM (
SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 7 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date`
FROM (SELECT 0 t0 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) t0,
(SELECT 0 t1 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) t1,
(SELECT 0 t2 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) t2,
(SELECT 0 t3 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) t3
) b
LEFT JOIN transactions a ON DATE(a.timestamp) <= b.date
WHERE b.date BETWEEN DATE(NOW()) - INTERVAL 6 DAY AND DATE(NOW())
GROUP BY b.date
ORDER BY b.date ASC
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=34f78e3d7c9225727ac2e728588759e2
To use alias in where clause, you must encapsulate your query in another select.
Example :
SELECT * FROM
(SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate FROM `transactions`) seb
WHERE seb.tDate <= v.`date`
Update try this:
SELECT v.*, t.*
FROM ( SELECT DATE(ADDDATE(DATE_SUB(NOW(),INTERVAL 7 DAY), t3*1000 + t2*100 + t1*10 + t0)) AS `date`
FROM (SELECT 0 t0 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) t0,
(SELECT 0 t1 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) t1,
(SELECT 0 t2 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) t2,
(SELECT 0 t3 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) t3 ) AS v
LEFT JOIN (SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate FROM transactions) AS t ON t.tDate = v.`date`
WHERE t.tDate <= v.`date` and v.`date` >= DATE_SUB(NOW(),INTERVAL 7 DAY) AND v.`date` <= DATE(NOW())
here tDate can be used
Firstly you need to understand that in this subquery
(SELECT SUM(quantity) AS stockOnDate, DATE(timestamp) as tDate
FROM `transactions`
WHERE tDate <= v.`date`) AS t
there is nothing defined as date or even the alias v. You're basically telling the query to look for v.date when it doesn't exists in the subquery.
I think this is what you're looking for:
SELECT dt, IFNULL(SUM(quantity),0) AS stockOnDate
FROM (SELECT STR_TO_DATE(CONCAT(LEFT(NOW(),7),LPAD(seq,2,0)), '%Y-%m%d') dt
FROM seq_1_to_31) v
LEFT JOIN `transactions` t ON dt=DATE(timestamp)
WHERE dt >= (SELECT MIN(DATE(timestamp)) FROM `transactions`)
AND dt <= (SELECT MAX(DATE(timestamp)) FROM `transactions`)
GROUP BY dt;
Here I'm using MariaDB's built-in sequence engine to generate days; concatenated with current year-month combination then uses STR_TO_DATE to make it recognizable as proper date format. The query generating those is in subquery v.
Second option is using a recursive common table expression to generate the date range based on existing date data in the table. Here is the query:
WITH RECURSIVE cte AS (
SELECT MIN(DATE(timestamp)) mndt, MAX(DATE(timestamp)) mxdt FROM `transactions`
UNION ALL
SELECT mndt+INTERVAL 1 DAY, mxdt FROM cte WHERE mndt+INTERVAL 1 DAY <= mxdt)
SELECT mndt, IFNULL(SUM(quantity),0) AS stockOnDate
FROM cte
LEFT JOIN `transactions` t ON mndt=DATE(timestamp)
GROUP BY mndt;
Both the query above eliminates the need to include a long subquery that generates the date range. This is the benefit of newer MySQL & MariaDB versions.
Demo fiddle
I have a database of users. I would like to create a graph based on userbase growth. The query I have now is:
SELECT DATE(datecreated), count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC
This returns almost what I want. If we get 0 users one day, that day is not returned as a 0 value, it is just skipped and the next day that has at least one user is returned. How can I get something like (psuedo-response):
date1 5
date2 8
date3 0
date4 0
date5 9
etc...
where the dates with zero show up in sequential order with the rest of the dates?
Thanks!
I hope you will figure out the rest.
select * from (
select date_add('2003-01-01 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date from
(select 0 as num
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,
(select 0 as num
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,
(select 0 as num
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,
(select 0 as num
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) n4,
(select 0 as num
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) n5
) a
where date >'2011-01-02 00:00:00.000' and date < NOW()
order by date
With
select n3.num*100+n2.num*10+n1.num as date
you will get a column with numbers from 0 to max(n3)*100+max(n2)*10+max(n1)
Since here we have max n3 as 3, SELECT will return 399, plus 0 -> 400 records (dates in calendar).
You can tune your dynamic calendar by limiting it, for example, from min(date) you have to now().
This question asks the same thing I think. Generally the accepted answer seems to be that you either do it in your application logic (read in what you have into an array, then loop through the array and create the missing dates), or you use temporary tables filled with the dates you wish to join.
This is better to do as:
-- 7 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 7 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc
-- 30 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 30 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 30 day) order by qb.day_series asc;
or without variable like this:
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select curdate() - INTERVAL a.a day as day_series 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
) as a ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc;
Do a right outer join to a table, call it tblCalendar, that is pre-populated with the dates you wish to report on. And join on the date field.
Paul
Query is:
SELECT qb.dy as yourday, COALESCE(count(yourcolumn), 0) as yourcount from yourtable qa
right join (
select curdate() as dy union
select DATE_SUB(curdate(), INTERVAL 1 day) as dy union
select DATE_SUB(curdate(), INTERVAL 2 day) as dy union
select DATE_SUB(curdate(), INTERVAL 3 day) as dy union
select DATE_SUB(curdate(), INTERVAL 4 day) as dy union
select DATE_SUB(curdate(), INTERVAL 5 day) as dy union
select DATE_SUB(curdate(), INTERVAL 6 day) as dy
) as qb
on qa.dates = qb.dy
and qa.dates > DATE_SUB(curdate(), INTERVAL 7 day)
order by qb.dy asc;
and the result is:
+------------+-----------+
| yourday | yourcount |
+------------+-----------+
| 2015-06-24 | 274339 |
| 2015-06-25 | 0 |
| 2015-06-26 | 0 |
| 2015-06-27 | 0 |
| 2015-06-28 | 134703 |
| 2015-06-29 | 87613 |
| 2015-06-30 | 0 |
+------------+-----------+
On further thought, something like this should be what you want:
CREATE TEMPORARY TABLE DateSummary1 ( datenew timestamp ) SELECT DISTINCT(DATE(datecreated)) as datenew FROM users;
CREATE TEMPORARY TABLE DateSummary2 ( datenew timestamp, number int ) SELECT DATE(datecreated) as datenew, count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC;
SELECT ds1.datenew,ds2.number FROM DateSummary1 ds1 LEFT JOIN DateSummary2 ds2 on ds1.datenew=ds2.datenew;
This gives you all the dates in the first table, and the count summary data in the second table. You might need to replace ds2.number with IF(ISNULL(ds2.number),0,ds2.number) or something similar.
Can I list somehow the dates of last 30 days in MySQL? Not from a table!
For example I think about like this:
SELECT date WHERE date BETWEEN SUBDATE(NOW(), INTERVAL 30 DAY) AND NOW();
Is that possible?
I hacked this together from someone else's code, but it seems to work:
SELECT DATE_FORMAT(m1, '%d %b %Y')
FROM (
SELECT SUBDATE( NOW() , INTERVAL 30 DAY) + INTERVAL m DAY AS m1
FROM (
select #rownum:=#rownum+1 as m from
(select 1 union select 2 union select 3 union select 4) t1,
(select 1 union select 2 union select 3 union select 4) t2,
(select 1 union select 2 union select 3 union select 4) t3,
(select 1 union select 2 union select 3 union select 4) t4,
(select #rownum:=-1) t0
) d1
) d2
WHERE m1 <= now()
ORDER BY m1
The original code by valex is here:
How to get a list of months between two dates in mysql
You can do this the "explicit" way. That is, generate a series of numbers and calculate the date:
select date(date_sub(now(), interval n.n day) as thedate
from (select 1 as n union all
select 2 union all
. . .
select 30
) n
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 );
I have a database of users. I would like to create a graph based on userbase growth. The query I have now is:
SELECT DATE(datecreated), count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC
This returns almost what I want. If we get 0 users one day, that day is not returned as a 0 value, it is just skipped and the next day that has at least one user is returned. How can I get something like (psuedo-response):
date1 5
date2 8
date3 0
date4 0
date5 9
etc...
where the dates with zero show up in sequential order with the rest of the dates?
Thanks!
I hope you will figure out the rest.
select * from (
select date_add('2003-01-01 00:00:00.000', INTERVAL n5.num*10000+n4.num*1000+n3.num*100+n2.num*10+n1.num DAY ) as date from
(select 0 as num
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,
(select 0 as num
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,
(select 0 as num
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,
(select 0 as num
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) n4,
(select 0 as num
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) n5
) a
where date >'2011-01-02 00:00:00.000' and date < NOW()
order by date
With
select n3.num*100+n2.num*10+n1.num as date
you will get a column with numbers from 0 to max(n3)*100+max(n2)*10+max(n1)
Since here we have max n3 as 3, SELECT will return 399, plus 0 -> 400 records (dates in calendar).
You can tune your dynamic calendar by limiting it, for example, from min(date) you have to now().
This question asks the same thing I think. Generally the accepted answer seems to be that you either do it in your application logic (read in what you have into an array, then loop through the array and create the missing dates), or you use temporary tables filled with the dates you wish to join.
This is better to do as:
-- 7 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 7 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc
-- 30 Days:
set #n:=date(now() + interval 1 day);
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select (select #n:= #n - interval 1 day) day_series from tbl1 limit 30 ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 30 day) order by qb.day_series asc;
or without variable like this:
SELECT qb.day_series as days , COALESCE(col_byte, 0) as Bytes from tbl1 qa
right join (
select curdate() - INTERVAL a.a day as day_series 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
) as a ) as qb
on date(qa.Timestamp) = qb.day_series and
qa.Timestamp > DATE_SUB(curdate(), INTERVAL 7 day) order by qb.day_series asc;
Do a right outer join to a table, call it tblCalendar, that is pre-populated with the dates you wish to report on. And join on the date field.
Paul
Query is:
SELECT qb.dy as yourday, COALESCE(count(yourcolumn), 0) as yourcount from yourtable qa
right join (
select curdate() as dy union
select DATE_SUB(curdate(), INTERVAL 1 day) as dy union
select DATE_SUB(curdate(), INTERVAL 2 day) as dy union
select DATE_SUB(curdate(), INTERVAL 3 day) as dy union
select DATE_SUB(curdate(), INTERVAL 4 day) as dy union
select DATE_SUB(curdate(), INTERVAL 5 day) as dy union
select DATE_SUB(curdate(), INTERVAL 6 day) as dy
) as qb
on qa.dates = qb.dy
and qa.dates > DATE_SUB(curdate(), INTERVAL 7 day)
order by qb.dy asc;
and the result is:
+------------+-----------+
| yourday | yourcount |
+------------+-----------+
| 2015-06-24 | 274339 |
| 2015-06-25 | 0 |
| 2015-06-26 | 0 |
| 2015-06-27 | 0 |
| 2015-06-28 | 134703 |
| 2015-06-29 | 87613 |
| 2015-06-30 | 0 |
+------------+-----------+
On further thought, something like this should be what you want:
CREATE TEMPORARY TABLE DateSummary1 ( datenew timestamp ) SELECT DISTINCT(DATE(datecreated)) as datenew FROM users;
CREATE TEMPORARY TABLE DateSummary2 ( datenew timestamp, number int ) SELECT DATE(datecreated) as datenew, count(*) AS number FROM users
WHERE DATE(datecreated) > '2009-06-21' AND DATE(datecreated) <= DATE(NOW())
GROUP BY DATE(datecreated) ORDER BY datecreated ASC;
SELECT ds1.datenew,ds2.number FROM DateSummary1 ds1 LEFT JOIN DateSummary2 ds2 on ds1.datenew=ds2.datenew;
This gives you all the dates in the first table, and the count summary data in the second table. You might need to replace ds2.number with IF(ISNULL(ds2.number),0,ds2.number) or something similar.