I have searched similar problems here on stackoverflow but I could not understand how to make this work, what I'm trying to do...
So, I want to get last 7 days transactions from database and get total sales amount and also include empty rows if there is no data for some day.
What I have so far:
http://sqlfiddle.com/#!2/f4eda/6
This outputs:
| PURCHASE_DATE | AMOUNT |
|---------------|--------|
| 2014-04-25 | 19 |
| 2014-04-24 | 38 |
| 2014-04-22 | 19 |
| 2014-04-19 | 19 |
What I want:
| PURCHASE_DATE | AMOUNT |
|---------------|--------|
| 2014-04-25 | 19 |
| 2014-04-24 | 38 |
| 2014-04-23 | 0 |
| 2014-04-22 | 19 |
| 2014-04-21 | 0 |
| 2014-04-20 | 0 |
| 2014-04-19 | 19 |
Any help appreciated :)
This is not easy. I took help from this thread generate days from date range and combined it with your query.
So the idea was to get the list of dates from last 7 days then left join these dates with a static amount 0 to the query you have and then finally sum them. This could be used for any date range, just need to change them in both the queries
select
t1.purchase_date,
coalesce(SUM(t1.amount+t2.amount), 0) AS amount
from
(
select DATE_FORMAT(a.Date,'%Y-%m-%d') as purchase_date,
'0' as amount
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.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
) a
where a.Date BETWEEN NOW() - INTERVAL 7 DAY AND NOW()
)t1
left join
(
SELECT DATE_FORMAT(purchase_date, '%Y-%m-%d') as purchase_date,
coalesce(SUM(amount), 0) AS amount
FROM transactions
WHERE purchase_date BETWEEN NOW() - INTERVAL 7 DAY AND NOW()
AND vendor_id = 0
GROUP BY purchase_date
)t2
on t2.purchase_date = t1.purchase_date
group by t1.purchase_date
order by t1.purchase_date desc
DEMO
Simply put together a subquery with the dates you want and use left outer join:
select d.thedate, coalesce(SUM(amount), 0) AS amount
from (select date('2014-04-25') as thedate union all
select date('2014-04-24') union all
select date('2014-04-23') union all
select date('2014-04-22') union all
select date('2014-04-21') union all
select date('2014-04-20') union all
select date('2014-04-19')
) d left outer join
transactions t
on t.purchase_date = d.thedate and vendor_id = 0
GROUP BY d.thedate
ORDER BY d.thedate DESC;
This is for last 7 days;
select d.thedate, coalesce(SUM(amount), 0) AS amount
from (select DATE(NOW()) as thedate union all
select DATE(DATE_SUB( NOW(), INTERVAL 1 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 2 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 3 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 4 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 5 DAY)) union all
select DATE(DATE_SUB( NOW(), INTERVAL 6 DAY))) d left outer join
transactions t
on t.purchase_date = d.thedate and vendor_id = 0
GROUP BY d.thedate
ORDER BY d.thedate DESC;
with recursive all_dates(dt) as (
select '2014-04-19' as dt
union all
select dt + interval 1 day
from all_dates
where dt + interval 1 day <= '2014-04-25'
)
select d.dt as purchase_date, coalesce(m.amount, 0) as purchased
from all_dates as d
left join mytable m
on d.dt = m.purchase_date
order by purchase_date desc;
Related
I have a table like:
id | date_start | date_end
1 | 2019.05.01 | 2019.05.06
2 | 2019.05.05 | 2019.05.05
3 | 2019.05.05 | 2019.05.08
Date_start and date_end means that entry is active during this period. Is it possible to get days with his count of active entries for intermediate values too? Like:
2019.05.01 | 1
2019.05.02 | 1
2019.05.03 | 1
2019.05.04 | 1
2019.05.05 | 3
2019.05.06 | 2
2019.05.07 | 1
2019.05.08 | 1
I think that this will work for you:
SELECT temp_date,
COUNT(*)
FROM (
SELECT ((SELECT MAX(date_start) FROM {dates_table}) - INTERVAL c.number DAY) AS temp_date
FROM (SELECT singles + tens + hundreds number
FROM ( SELECT 0 singles
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
) singles
JOIN (SELECT 0 tens
UNION ALL SELECT 10 UNION ALL SELECT 20 UNION ALL SELECT 30
UNION ALL SELECT 40 UNION ALL SELECT 50 UNION ALL SELECT 60
UNION ALL SELECT 70 UNION ALL SELECT 80 UNION ALL SELECT 90
) tens
JOIN (SELECT 0 hundreds
UNION ALL SELECT 100 UNION ALL SELECT 200 UNION ALL SELECT 300
UNION ALL SELECT 400 UNION ALL SELECT 500 UNION ALL SELECT 600
UNION ALL SELECT 700 UNION ALL SELECT 800 UNION ALL SELECT 900
) hundreds
ORDER BY number DESC) c
WHERE c.number BETWEEN 0 AND (SELECT DATEDIFF(MAX(date_end),MIN(date_start)) FROM {dates_table})
) calendar
JOIN {dates_table} ON temp_date BETWEEN end_date AND start_date
GROUP BY temp_date
Just replace {dates_table} with the name of your table.
The base of this that generates the series of dates was taken from IvanD's answer on this SO question: How to populate a table with a range of dates?
I'm having a table Storing planned_production below Format.I need to get the working_dates for current week.
+------------+-------------+---------+----------+---------+------+--------------+---------------------------------------------+
| planned_id | per_quarter | per_day | per_week | quarter | year | working_days | working_dates | |
+------------+-------------+---------+----------+---------+------+--------------+---------------------------------------------+
| 1 | 860 | 14.10 | 70.50 | 2 | 2018 | 61 | 02-04-2018,03-04-2018,04-04-2018,05-04-2018 |
| 06-04-2018,09-04-2018,12-04-2018,15-04-2018
+------------+-------------+---------+----------+---------+------+--------------+---------------------------------------------+
I'm trying to get the working dates for current week.
the query i'm using is below.
i'm not able to figure it out where i'm going wrong.
SELECT Value from ( SELECT SUBSTRING_INDEX(SUBSTRING_INDEX(working_dates, ',', n.n), ',', -1) Value
FROM planned_production t CROSS JOIN
(
SELECT a.N + b.N * 10 + 1 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) a
,(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) b
ORDER BY n
) n
WHERE quarter = quarter(curdate()) and n.n <= 1 + (LENGTH(t.working_dates) - LENGTH(REPLACE(t.working_dates, ',', '')))
) as TEMP_TABLE
WHERE Value between CURRENT_DATE() - INTERVAL
WEEKDAY(CURRENT_DATE()) + 7 DAY
AND
(CURRENT_DATE() - INTERVAL WEEKDAY(CURRENT_DATE()) DAY) - INTERVAL 1
SECOND ORDER BY Value asc;
I need something like this as output
+------------+
| value |
+------------+
| 17-05-2018 |
| 18-05-2018 |
+------------+
Any suggestions welcomed and Thanks in advance.
Table Structure:
create table test (planned_id int, per_quarter float, per_day float, per_week float,
quarter int, year int, working_days int, working_dates varchar(1000));
Table Values:
insert into test values ( 1,860,14.10,70.50,2,2018,61,'02-04-2018,03-04-2018,04-04-2018,05-04-2018,06-04-2018,09-04-2018,12-04-2018,15-04-2018,17-05-2018,18-05-2018');
insert into test values ( 2,800,12.10,60.50,2,2018,50,'02-04-2018,03-04-2018,04-04-2018,05-04-2018,06-04-2018,09-04-2018,12-04-2018,15-04-2018,14-05-2018,15-05-2018') ;
SQL Query:
set #startdate := date_add(SUBDATE(current_date, WEEKDAY(current_date)), interval -1 day);
select test.planned_id, checkdate
from
(
select #startdate := date_add(#startdate, interval 1 day) as checkdate
from ( SELECT 0 singles
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)
a cross join (
SELECT 0 singles
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) b
cross join (
SELECT 0 singles
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) c
) as dim_date
join test
where
checkdate between SUBDATE(current_date, WEEKDAY(current_date))
and date_add(SUBDATE(current_date, WEEKDAY(current_date)) , interval 7 day)
and find_in_set(DATE_FORMAT(checkdate, '%d-%m-%Y'),working_dates) != 0
order by test.planned_id
Output:
planned_id checkdate
1 2018-05-17
1 2018-05-18
2 2018-05-14
2 2018-05-15
Logic:
1) First create table dim_date. You can create it using query as shown in above query but I would suggest you to create one table in your database.
2) Use filter to select only one week
checkdate between SUBDATE(current_date, WEEKDAY(current_date))
and date_add(SUBDATE(current_date, WEEKDAY(current_date)) , interval 7 day)
3) Use cross join between dim_table and test
4) Use find_in_set function to check date is present or not.
and find_in_set(DATE_FORMAT(checkdate, '%d-%m-%Y'),working_dates) != 0
I have a table which has start and end date. Based on that table, I have to get the active orders for the past 12 months.
In end date NULL means it is active order until now.
Table Basic idea
Expected Output
Based on the past experience I'm able to write the query like this for the current year January but i need it for all the months as shown in the above picture
select
mnth.num, count(*)
from (
select 1 AS num 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
) mnth
left join (
select
o.id
, case
when o.start_date < date_format(current_date(), '%Y-01-01') then 1
else month(o.start_date)
end AS start_month
, case
when o.end_date < date_format(current_date(), '%Y-01-01') then 0
when o.end_date >= date_format(current_date(), '%Y-01-01') then month(o.end_date)
else month(current_date())
end AS end_month
from order o
) active_work_orders on mnth.num between active_work_orders.start_month and active_work_orders.end_month
where mnth.num <= month(current_date())
group by
mnth.num
;
Based on the output I have to generate a graph. Kindly help on this
Fiddle link click here
Maybe something like this
select mthnum,
count(*)
from
(
select mthnum, o.*,year(start_date)* 12 + month(start_date),year(end_date) * 12 + month(end_date)
from
(
#select 2016*12 + 11 AS mthnum union all select 2016*12 + 12 union all select 2017*12 + 1 union all select 2017*12 + 2
select 2017*100 + 2 as mthnum
union all select 2017*100 + 3 union all select 2017*100 + 4 union all select 2017*100 + 5 union all select 2017*100 + 6
union all select 2017*100 + 7 union all select 2017*100 + 8 union all select 2017*100 + 9 union all select 2017*100 + 10
union all select 2017*100 + 11 union all select 2017*100 + 12 union all select 2018*100 + 1
) mth
cross join
(select * from order_test_new #where order_num = 1288
) o
) s where (s.mthnum >= year(s.start_date)*100 + month(start_date)) and
(end_date is null or s.mthnum <= year(end_date) * 100 + month(s.end_date))
group by mthnum
result
+--------+----------+
| mthnum | count(*) |
+--------+----------+
| 201702 | 9 |
| 201703 | 9 |
| 201704 | 9 |
| 201705 | 8 |
| 201706 | 8 |
| 201707 | 8 |
| 201708 | 8 |
| 201709 | 7 |
| 201710 | 7 |
| 201711 | 6 |
| 201712 | 6 |
| 201801 | 6 |
+--------+----------+
12 rows in set (0.00 sec)
This considers that an order with and end_date is active in the end_date month.
You can take the help of extract function to extract year and month and do a group by based on that.
Then, for displaying, you can use date_format function.
So, here it goes:
SELECT
DATE_FORMAT(start_date, '%b-%y') AS `Month`, COUNT(*) AS `Active`
FROM
orders
WHERE
end_date IS NULL
GROUP BY EXTRACT(YEAR_MONTH FROM start_date)
For getting result from last 12 months only, you can use the following:
SELECT
`month_list`.`month`, IFNULL(`active_orders`.`active_count`, 0) AS `active_count`
FROM
(SELECT
*
FROM
(SELECT DATE_FORMAT(NOW(), '%b-%y') AS `month`
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 1 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 2 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 3 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 4 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 5 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 6 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 7 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 8 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 9 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 10 MONTH, '%b-%y')
UNION
SELECT DATE_FORMAT(NOW() - INTERVAL 11 MONTH, '%b-%y'))
AS `months`) as `month_list`
LEFT JOIN
(SELECT
DATE_FORMAT(start_date, '%b-%y') AS `month`, COUNT(*) AS `active_count`
FROM
`order`
WHERE
end_date IS NULL
GROUP BY EXTRACT(YEAR_MONTH FROM start_date)) as `active_orders` on `month_list`.`month` = `active_orders`.`month`;
How to calculate the number of unique days per month from a table with two date columns, in which the periods can have gaps and overlaps?
I rather not use a calendar table to get the unique days, because it generates a temporary table with thousands of records, and resources are limited.
Example table:
+---------+------------+------------+
| mygroup | alpha | omega |
+---------+------------+------------+
| 1 | 2017-02-04 | 2017-04-14 |
| 1 | 2017-03-25 | 2017-03-28 |
| 1 | 2017-01-23 | 2017-01-25 |
| 2 | 2017-02-05 | 2017-02-20 |
| 1 | 2017-04-28 | 2017-05-12 |
| etc. | etc. | etc. |
+---------+------------+------------+
Is it what you need?
select count(distinct selected_date),te.mygroup, MONTHNAME(selected_date)from
(select adddate('1970-01-01',t4.i*10000 + t3.i*1000 + t2.i*100 + t1.i*10 + t0.i) selected_date 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) t0,
(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) t1,
(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) t2,
(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) t3,
(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) t4) v
cross join test te
where selected_date between te.alpha and te.omega
group by mygroup, MONTHNAME(selected_date)
Оutput for your example:
'17','1','April'
'25','1','February'
'3','1','January'
'31','1','March'
'12','1','May'
'16','2','February'
Count may be greater than number of the days in month because such overlap exists in few rows - it's not а mistake.
There is another way to do this roughly 10 times faster than a calendar table.
The biggest resource spoiler is the calendar table itself, it is used to filter unique days.
But instead of using a whole table record for that, it can be done using 31 bits in an UINT.
Recepe:
Create a calendar table with months only
Cut periods in months, and join them with the calendar table
Translate periods to bits of UINTs
OR the UINTs per month for uniqueness
Count their bits as unique days per month
Output:
+--------------+---------+---------+-------+
| Period | Group 1 | Group 2 | Total |
+--------------+---------+---------+-------+
| 2017 month 5 | 11 | 0 | 11 |
| 2017 month 4 | 15 | 0 | 15 |
| 2017 month 3 | 30 | 0 | 30 |
| 2017 month 2 | 24 | 15 | 39 |
| 2017 month 1 | 2 | 0 | 2 |
+--------------+---------+---------+-------+
MySQL query:
SELECT
`tabulate`.`period` AS `Period`,
SUM(IF(`tabulate`.`mygroup` = 1,
`tabulate`.`days`, 0)) AS `Group 1`,
SUM(IF(`tabulate`.`mygroup` = 2,
`tabulate`.`days`, 0)) AS `Group 2`,
SUM(`tabulate`.`days`) AS `Total`
FROM
( SELECT
`unique`.`period`,
BIT_COUNT(BIT_OR(CONV(CONCAT(
REPEAT("1", DAYOFMONTH(`unique`.`omega`) - DAYOFMONTH(`unique`.`alpha`)),
REPEAT("0", DAYOFMONTH(`unique`.`alpha`) - 1)
), 2, 10))) AS `days`,
`unique`.`mygroup`
FROM
( SELECT
DATE_FORMAT(`permonth`.`period_alpha`, "%Y month %c") AS `period`,
GREATEST(`permonth`.`period_alpha`, `permonth`.`example_alpha`) AS `alpha`,
LEAST(`permonth`.`period_omega`, `permonth`.`example_omega`) AS `omega`,
`permonth`.`mygroup`
FROM
( SELECT
`period`.`alpha` AS `period_alpha`,
DATE_SUB(`period`.`omega`, INTERVAL 1 DAY) AS `period_omega`,
`example`.`mygroup`,
IFNULL(`example`.`alpha`, `period`.`alpha`) AS `example_alpha`,
IFNULL(`example`.`omega`, CURDATE()) AS `example_omega`
FROM
( SELECT
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL `season`.`n` + (`month`.`n` << 2) MONTH
) AS `alpha`,
DATE_ADD(
MAKEDATE(YEAR(CURDATE()), 1),
INTERVAL 1 + `season`.`n` + (`month`.`n` << 2) MONTH
) AS `omega`
FROM
( SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
) AS `month`
CROSS JOIN (SELECT 0 AS `n`
UNION ALL SELECT 1
UNION ALL SELECT 2
UNION ALL SELECT 3
) AS `season`
) AS `period`
INNER JOIN
( SELECT 1 AS `mygroup`, "2017-02-04" AS `alpha`, "2017-04-14" AS `omega`
UNION ALL SELECT 1, "2017-03-25", "2017-03-28"
UNION ALL SELECT 1, "2017-01-23", "2017-01-25"
UNION ALL SELECT 2, "2017-02-05", "2017-02-20"
UNION ALL SELECT 1, "2017-04-28", "2017-05-12"
) AS `example` ON (
(`example`.`alpha` < `period`.`omega` OR `example`.`alpha` IS NULL)
AND IFNULL(`example`.`omega`, CURDATE()) >= `period`.`alpha`
)
) AS `permonth`
) AS `unique`
GROUP BY
`unique`.`period`,
`unique`.`mygroup`
) AS `tabulate`
GROUP BY `tabulate`.`period`
ORDER BY `tabulate`.`period` DESC
I am trying to make a query that will result in a list showing how many active users we have month by month. We define an active users as a user that have been logged in in the last 90 days.
I can easily define the number of active users we have right now by this
SELECT COUNT(DISTINCT(user_id) FROM login_table
WHERE login_date BETWEEN DATE_SUB(login_date, INTERVAL 90 DAY) AND NOW())
My problem comes when I have to count the amount of users we have month by month
Here i have to count one single login several times.
If I have a user that login at 10 of January, and never login again, this user should count as an active user in the following months: January, February, March and April, even though I only have one registration of the user
Example data:
login_date | user_id
2015-01-01 | 1
2015-02-10 | 1
2015-02-11 | 2
2015-02-13 | 1
2015-03-19 | 1
This should result in something like this:
Date | Active users
2015-01 | 1
2015-02 | 2
2015-03 | 2
2015-04 | 2
2015-05 | 2
2015-06 | 1
2015-07 | 0
2015-08 | 0
Is there anyway of doing a count like that?
Problems like this are easier to solve, if you have a calendar table with all the dates you need. If you don't have such a table you can create it with a query like this:
create table `calendar` (
`date` DATE NOT NULL,
PRIMARY KEY (`date`)
)
select DATE_ADD('1900-01-01',INTERVAL t4.c*10000 + t3.c*1000 + t2.c*100 + t1.c*10 + t0.c DAY) as `date`
from
(select 0 c 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) t0,
(select 0 c 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) t1,
(select 0 c 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) t2,
(select 0 c 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) t3,
(select 0 c 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) t4
That will create a table with dates from 1900-01-01 to 2173-10-15 (100K days) and consume only about 2.5 MB. You can adjust it to your needs.
Using the caledar table you can get three-month-ranges:
select
DATE_FORMAT(date_sub(c.date, INTERVAL 1 day), '%Y-%m') as month,
date_sub(c.date, INTERVAL 3 month) as first_day,
date_sub(c.date, INTERVAL 1 day) as last_day
from calendar c
where day(c.date) = 1
and c.date between '2015-02-01' and '2015-09-01'
Result:
| month | first_day | last_day |
| 2015-01 | 2014-11-01 | 2015-01-31 |
| 2015-02 | 2014-12-01 | 2015-02-28 |
| 2015-03 | 2015-01-01 | 2015-03-31 |
| 2015-04 | 2015-02-01 | 2015-04-30 |
| 2015-05 | 2015-03-01 | 2015-05-31 |
| 2015-06 | 2015-04-01 | 2015-06-30 |
| 2015-07 | 2015-05-01 | 2015-07-31 |
| 2015-08 | 2015-06-01 | 2015-08-31 |
Adjust it, if you really want to use something like 90 days intervals.
Now it's a simple left join with the login table to get what you want:
select i.month as `Date`, count(distinct l.user_id) as `Active users`
from (
select
date_format(date_sub(c.date, interval 1 day), '%Y-%m') as month,
date_sub(c.date, interval 3 month) as first_day,
date_sub(c.date, interval 1 day) as last_day
from calendar c
where day(c.date) = 1
and c.date between '2015-02-01' and '2015-09-01'
) i
left join login_table l on l.login_date between i.first_day and i.last_day
group by i.month
http://sqlfiddle.com/#!9/d1bb0/3
Can you use:
Select DATE_FORMAT(login_date,'%Y %m') as date, ....
....
Group by DATE_FORMAT(login_date,'%Y %m')
You can build an in-line tally table containing all the required month/year value pairs. Then LEFT JOIN your table to this table and GROUP BY to get the required result:
SELECT CONCAT(months.m, '-', years.y) AS 'date',
COUNT(DISTINCT(user_id)) AS 'users_count'
FROM (
SELECT '01' AS m UNION ALL SELECT '02' UNION ALL SELECT '03' UNION ALL
SELECT '04' UNION ALL SELECT '05' UNION ALL SELECT '06' UNION ALL
SELECT '07' UNION ALL SELECT '08' UNION ALL SELECT '09' UNION ALL
SELECT '10' UNION ALL SELECT '11' UNION ALL SELECT '12') AS months
CROSS JOIN (
SELECT '2015' AS y UNION ALL SELECT '2016') AS years
LEFT JOIN login_table AS lt
ON DATE_FORMAT(lt.login_date, '%d-%Y') = CONCAT(months.m, '-', years.y)
GROUP BY CONCAT(months.m, '-', years.y)
The above query is suitable for producing a report for all the months from years 2015/2016. You can edit the tally table as you wish in order to adapt it to your actual requirement.
Edit:
You need correlation if you want to count active users in rolling 3 month intervals:
SELECT CONCAT(months.m, '-', years.y) AS 'date',
(SELECT COUNT(DISTINCT(user_id))
FROM login_table
WHERE login_date BETWEEN CONCAT(years.y, '-', months.m, '-01') AND
DATE_ADD(CONCAT(years.y, '-', months.m, '-01'), INTERVAL 90 DAY))
FROM (
SELECT '01' AS m UNION ALL SELECT '02' UNION ALL SELECT '03' UNION ALL
SELECT '04' UNION ALL SELECT '05' UNION ALL SELECT '06' UNION ALL
SELECT '07' UNION ALL SELECT '08' UNION ALL SELECT '09' UNION ALL
SELECT '10' UNION ALL SELECT '11' UNION ALL SELECT '12') AS months
CROSS JOIN (
SELECT '2015' AS y UNION ALL SELECT '2016') AS years
LEFT JOIN login_table AS lt
ON DATE_FORMAT(lt.login_date, '%d-%Y') = CONCAT(months.m, '-', years.y)
GROUP BY CONCAT(months.m, '-', years.y)
and by using the method MONTH()?
something like this:
SELECT MONTH(login-date) as perMonth, COUNT(DISTINCT(user_id)) from login_table GROUP BY MONTH(login-date)
SELECT Date_format(login_date, '%Y-%m-%01') as mymonth, count(user_id) as totalusers
FROM login_table
WHERE login_date >= DATE_SUB(now(), INTERVAL 90 DAY)
Group by Date_format(login_date, '%Y-%m-%01')
Here is info about date_format function https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_date-format
This will work:
SELECT DATE_FORMAT(login_date, "%Y-%m"), COUNT(user_id)
FROM login_table
WHERE login_date BETWEEN DATE_SUB(login_date, INTERVAL 90 DAY) AND NOW()
GROUP BY login_date
Another simple and stupid solution is to copy the login_table six times adding 15 days to the login_date each time. This way we can ensure that every login is duplicated at least once in every month within the following 90 days. Then we can group by year and moth to count active users.
select
date_format(login_date, "%Y-%m") as `Date`,
count(distinct l.user_id) as `Active users`
from (
select l.user_id, l.login_date from login_table l
union all select l.user_id, date_add(l.login_date, interval 15 day) from login_table l
union all select l.user_id, date_add(l.login_date, interval 30 day) from login_table l
union all select l.user_id, date_add(l.login_date, interval 45 day) from login_table l
union all select l.user_id, date_add(l.login_date, interval 60 day) from login_table l
union all select l.user_id, date_add(l.login_date, interval 75 day) from login_table l
union all select l.user_id, date_add(l.login_date, interval 90 day) from login_table l
) l
group by date_format(login_date, "%Y-%m")