I'm trying to do a rolling count of registration growth on a student website.
The query looks as follows:
SELECT COUNT(type) as student_count, MONTH(created_at) as month, YEAR(created_at) as year
FROM users
WHERE type = 'student'
GROUP BY MONTH(created_at), YEAR(created_at)
ORDER BY year, month
This produces the following output:
What I'm trying to achieve in the query is to keep adding up the student_counts from the previous rows.
So:
December 2014 should have 15 students
January 2015 should have 16 students
February 2015 should have 34 students
and so on...
Is this possible in SQL or is it better to do this when outputting the data in the code itself?
select *, #sum := #sum + student_count as sum
from
(
SELECT YEAR(created_at) as year,
MONTH(created_at) as month,
COUNT(type) as student_count
FROM users
WHERE type = 'student'
GROUP BY year, month
ORDER BY year, month
) tmp
CROSS JOIN (select #sum := 0) s
Try using with rollup
SELECT COUNT(type) as student_count, MONTH(created_at) as month, YEAR(created_at) as year
FROM users
WHERE type = 'student'
GROUP BY YEAR(created_at), MONTH(created_at) WITH ROLLUP
Try this:
SELECT #cumulative := 0;
SELECT #cumulative := #cumulative + student_count student_count,
month, year
FROM (
SELECT COUNT(type) as student_count,
MONTH(created_at) as month,
YEAR(created_at) as year
FROM users
WHERE type = 'student'
GROUP BY MONTH(created_at), YEAR(created_at)
) A ORDER BY year, month
One approach to handle this in MySQL uses a correlated subquery to find the running total.
SELECT DISTINCT
(SELECT COUNT(*) FROM users u2
WHERE DATE_FORMAT(u2.created_at, '%Y-%m') <=
DATE_FORMAT(u1.created_at, '%Y-%m')) AS student_count,
DATE_FORMAT(created_at, '%Y-%m') AS ym
FROM users u1
WHERE type = 'student'
ORDER BY DATE_FORMAT(created_at, '%Y-%m');
Demo
Not much to explain here, except that SELECT DISTINCT gives us each unique year-month value in the table as a single record. We then count all rows at that point in time or earlier to find the running total.
Related
I currently am trying to track the number of messages sent by month as well as the volume's percent change in comparison to one year prior.
Here is my current query:
Select
a.mo,
a.ye,
a.Messages,
((a.Messages - b.Messages) / b.Messages) as "% Change"
from(
select
MONTH(post_date) as mo,
count(*) as "Messages",
YEAR(post_date) as ye
from
pm_messages
WHERE
post_date > "2018-01-01 00:00:00"
group by
year(post_date),
month(post_date)
) a
left join (
select
MONTH(post_date) as mo,
YEAR(post_date) as ye,
count(*) as "Messages"
from
pm_messages
group by
year(post_date),
month(post_date)
) b on a.mo = b.mo
and a.ye -1 = b.ye
This works great, however, it places month and year in separate columns, which has been messing up the graphs I am working with. However, when I try to pull month and year into one columns as I've done in other queries from the same table, i.e. using:
SELECT DATE_FORMAT(`post_date`,'%M %Y')
My query does not work.
Does anyone know how I can combine my current query to still calculate the return from a year prior but have month and date come up as one column, as opposed to (Month | Year | Messages | % Change)
Thanks!!
you can use extract instead of separate year() and month() functions :
EXTRACT(YEAR_MONTH from post_date)
of course you have to group by this instead of year, month . for example :
select
EXTRACT(YEAR_MONTH from post_date) yearmonth,
count(*) as "Messages"
from
pm_messages
group by
EXTRACT(YEAR_MONTH from post_date)
If you have data for every month, you can use lag():
select year(post_date) as ye, month(post_date) as mo,
count(*) as Messages,
lag(count(*)) over (partition by month(post_date) order by year(post_date)) as prev_year
from pm_messages
where post_date >= '2018-01-01'
group by year(post_date), month(post_date)
I want to get a statistic for every month of the years i have in DB
SELECT monthname(created_at) AS month, YEAR(created_at) AS year, count(*) AS number
FROM tableName
WHERE type_of_user = "someType"
GROUP BY year, month(created_at)
ORDER BY created_at DESC
Now it gives me only month that I have, but I need to get statistics for every month, even if I don't have any stored data for that month
Create a calendar table. This will need one entry per month, for every year that you intend to use.
Then select from the calendar table, and join in the values that you get from your current query. Use COALESCE() to put a zero-value where the entry is NULL (e.g. when there are no records in the tableName for that month and year).
SELECT MONTHNAME(date) as month,
YEAR(date) as year,
COALESCE(number, 0) as number
FROM calendar AS C
LEFT JOIN (
SELECT created_at, COUNT(*) as number
FROM tableName AS T
WHERE T.type_of_user = 'someType'
GROUP BY YEAR(created_at), MONTH(created_at)
) AS T
ON MONTH(T.created_at) = MONTH(C.date) AND YEAR(T.created_at) = YEAR(C.date)
GROUP BY month, YEAR(created_at)
ORDER BY MONTH(date), YEAR(date)
SQL fiddle at http://sqlfiddle.com/#!9/e0a4dc/
SELECT month(created_at) as month
FROM tableName
RIGHT JOIN (select row_number() over (order by 1) as i
from someTableWithMoreThan12Records limit 12) x
ON x.i=month(created_at)
ORDER BY I;
JOINing with a table that has all the records will give you every month.
Basically I have a table like this:
Table Time:
ID.......Date
1......08/26/2016
1......08/26/2016
2......05/29/2016
3......06/22/2016
4......08/26/2015
5......05/23/2015
5......05/23/2015
6......08/26/2014
7......04/26/2014
8......08/26/2013
9......03/26/2013
The query should return like this
Year........CountNum
2016........4
2015........3
To find out which year does its value tend to increase in. I notice that I want to display the years that have more values (number of row in this case) than the previous year.
What I've done so far
SELECT Year, count(*) as CountNum
FROM Time
GROUP BY Year
ORDER BY CountNum DESC;
I don't know how to get the year from date format. I tried year(Date) function, but I got Null data.
Please help!
It should works fine.
select year(date), count(*) as countNum
from time
group by year(date)
order by countNum
Join the grouped data to itself with 1 year offset:
select
a.*
from
(
select year(`Date`) as _year, count(*) as _n
from time group by 1
) a
left join
(
select year(`Date`) as _year, count(*) as _n
from time group by 1
) b
on a._year = b._year-1
where a._n > b._n
order by 1
I need to get the total number of an item for every month.
So far, with the following code :
Select Count(mpay_collector_company.id) As `Number of Collector Companies`,
Month(mpay_collector_company.created_at) As Month,
Year(mpay_collector_company.created_at) As Year
From mpay_collector_company
Group By Month(mpay_collector_company.created_at),
Year(mpay_collector_company.created_at)
I have the following response :
#|Year|Month
------------
5|2014|11
3|2014|12
3|2015|1
7|2015|2
6|2015|3
2|2015|4
5|2015|6
1|2015|7
And instead of the number for each month I'd like to have the sum from the beginning for each month, which would be something like this :
Sum|Year|Month
--------------
5|2014|11
8|2014|12
11|2015|1
18|2015|2
24|2015|3
26|2015|4
31|2015|6
32|2015|7
Any ideas ?
EDIT : This request will be implemented as a view, so sub-requests are pretty much a no-go :x
Something like this could work:
Select Count(distinct previous_mpay_collector_company.id)+ Count(distinct mpay_collector_company.id) As `Number of Collector Companies`,
Month(mpay_collector_company.created_at) As Month,
Year(mpay_collector_company.created_at) As Year
From mpay_collector_company
Left Join mpay_collector_company previous_mpay_collector_company
On Year(mpay_collector_company.created_at) > Year(previous_mpay_collector_company.created_at)
OR (Month(mpay_collector_company.created_at) > Month(previous_mpay_collector_company.created_at)
And Year(mpay_collector_company.created_at) >= Year(previous_mpay_collector_company.created_at))
And previous_mpay_collector_company.id <> mpay_collector_company.id
Group By Month(mpay_collector_company.created_at),
Year(mpay_collector_company.created_at);
The trick would be self joining to all of the previous months and counting from the joined table.
You can first select distinct year.months and then check count for each value
select year,month ,
( SELECT count(*) FROM mpay_collector_company WHERE
Year(mpay_collector_company.created_at) <= temp1.year
AND
Month(mpay_collector_company.created_at) <= temp1.month
)
from (
Select DISTINCT
Month(mpay_collector_company.created_at) As month,
Year(mpay_collector_company.created_at) As year
From mpay_collector_company
) as temp1
ORDER BY year,month
if you want to "reset" for each year , just change comparison to EQUAL between years and thats it
select year,month ,
( SELECT count(*) FROM mpay_collector_company WHERE
Year(mpay_collector_company.created_at) = temp1.year
AND
Month(mpay_collector_company.created_at) <= temp1.month
)
from (
Select DISTINCT
Month(mpay_collector_company.created_at) As month,
Year(mpay_collector_company.created_at) As year
From mpay_collector_company
) as temp1
ORDER BY year,month
Using a correlated subquery to count the number of rows with a date before the current should work too:
Select
Count(id) As "Number of Collector Companies",
Month(created_at) As Month,
Year(created_at) As Year,
(
Select Count(*)
From mpay_collector_company
Where EXTRACT(YEAR_MONTH from created_at) <= EXTRACT(YEAR_MONTH from m.created_at)
) as running_count
From mpay_collector_company m
Group By Year(created_at), Month(created_at);
Sample SQL Fiddle showing it in action
Select
(Select Sum(Count(Z.id)) FROM mpay_collector_company z
where z.created_at <= A.created_at) as '#',
Month(A.created_at) As Month,
Year(A.created_at) As Year
From mpay_collector_company A
Group By Month(mpay_collector_company.created_at),
Year(mpay_collector_company.created_at)
This should do it for ya,
SELECT
Day,
month,
year,
GROUP_CONCAT(total),
GROUP_CONCAT(SP_ID)
FROM
(
SELECT
DATE_FORMAT(l.act_date, '%d') AS DAY,
DATE_FORMAT(l.act_date, '%M') AS MONTH,
EXTRACT(YEAR FROM l.act_date) AS YEAR,
COUNT(*) as total,l.sp_id
FROM lead_activity2 as l
right outer join salesperson as s on l.sp_id=s.sp_id
WHERE l.act_name='scb'
AND ((l.act_date>='2012-09-07 13:03:27' )
AND (l.act_date<= '2012-11-07 13:03:27'))
GROUP BY MONTH, YEAR, DAY, l.sp_id
ORDER BY YEAR DESC, MONTH DESC, DAY DESC, l.sp_id DESC
) t GROUP BY day, month, year
http://sqlfiddle.com/#!2/1514d/3 - you can view the scheme and the query,
what i would like to get is
18 | october | 2012 | 0,0,1,1 | 6,5,4,3
spid 6 and spid 5 have no data for 18 october but still should be shown tried doing right join and right outer join both dont seem to work...
Use GROUP_CONCAT like so:
SELECT
Day,
month,
year,
GROUP_CONCAT(total),
GROUP_CONCAT(SP_ID)
FROM
(
SELECT
DATE_FORMAT(l.act_date, '%d') AS DAY,
DATE_FORMAT(l.act_date, '%M') AS MONTH,
EXTRACT(YEAR FROM l.act_date) AS YEAR,
COUNT(*) as total,l.sp_id
FROM lead_activity2 as l
WHERE l.act_name='scb'
AND ((l.act_date>='2012-09-07 13:03:27' )
AND (l.act_date<= '2012-11-07 13:03:27'))
GROUP BY MONTH, YEAR, DAY, l.sp_id
ORDER BY YEAR DESC, MONTH DESC, DAY DESC, l.sp_id DESC
) t GROUP BY day, month, year
Updated SQL Fiddle
Update: Yes you can do this, but use LEFT JOIN to include non matching sp_id. These non matching ids will have a value of NULL use IFNULL to display it with zeros like so:
SELECT
Day,
month,
year,
GROUP_CONCAT(total) Total,
GROUP_CONCAT(SP_ID) 'List of sp_ids'
FROM
(
SELECT
DATE_FORMAT(l.act_date, '%d') AS DAY,
DATE_FORMAT(l.act_date, '%M') AS MONTH,
EXTRACT(YEAR FROM l.act_date) AS YEAR,
COUNT(*) as total,
IFNULL(s.sp_id , 0) sp_id
FROM lead_activity2 as l
LEFT JOIN salesperson s ON l.sp_id = s.sp_id
WHERE l.act_name='scb'
AND ((l.act_date>='2012-09-07 13:03:27' )
AND (l.act_date<= '2012-11-07 13:03:27'))
GROUP BY MONTH, YEAR,DAY,s.sp_id
) t
ORDER BY YEAR DESC,
MONTH DESC,
DAY DESC,
sp_id DESC
Updates SQL Fiddle Demo