How to preserve grouping in subquery - mysql

Hello i have a sql query like this:
SELECT
COUNT(*) AS total,
COUNT(referrer) AS referrer,
DATE_FORMAT(created_at, "%Y") AS YEAR,
DATE_FORMAT(created_at, "%m") AS MONTH,
DATE_FORMAT(created_at, "%d") AS DAY
FROM
users
GROUP BY
EXTRACT(DAY FROM created_at),
EXTRACT(MONTH FROM created_at),
EXTRACT(YEAR FROM created_at)
ORDER BY
created_at ASC
This gives me table with totals sorted by day (month and year). what i need is to add a subquery to display one more count() which has a condition. Something like:
(SELECT count(*) FROM users WHERE result = '1') as winners
the problem obviously is with the grouping of main query since i get same result without grouping the subquery for every row.
What would be the right way to execute such query?

How about
SELECT
COUNT(*) AS total,
COUNT(referrer) AS referrer,
SUM(case when result = '1' then 1 else 0 end) as winners
DATE_FORMAT(created_at, "%Y") AS YEAR,
DATE_FORMAT(created_at, "%m") AS MONTH,
DATE_FORMAT(created_at, "%d") AS DAY
FROM
users
GROUP BY
EXTRACT(DAY FROM created_at),
EXTRACT(MONTH FROM created_at),
EXTRACT(YEAR FROM created_at)
ORDER BY
created_at ASC;
Assuming created_at is a timestamp, we could re-write your query with just one field to group by as follows:
SELECT
COUNT(*) AS total,
COUNT(referrer) AS referrer,
SUM(case when result = '1' then 1 else 0 end) as winners
DATE_FORMAT(created_at, "%Y") AS YEAR,
DATE_FORMAT(created_at, "%m") AS MONTH,
DATE_FORMAT(created_at, "%d") AS DAY
FROM
users
GROUP BY date(created_at)
ORDER BY
created_at ASC;
Please note I do not have mysql to hand.

You can join both queries with UNION
SELECT
COUNT(*) AS total,
COUNT(referrer) AS referrer,
DATE_FORMAT(created_at, "%Y") AS YEAR,
DATE_FORMAT(created_at, "%m") AS MONTH,
DATE_FORMAT(created_at, "%d") AS DAY
FROM
users
GROUP BY
EXTRACT(DAY FROM created_at),
EXTRACT(MONTH FROM created_at),
EXTRACT(YEAR FROM created_at)
ORDER BY
created_at ASC
UNION
SELECT count(*) FROM users
GROUP BY
EXTRACT(DAY FROM created_at),
EXTRACT(MONTH FROM created_at),
EXTRACT(YEAR FROM created_at)
HAVING result = '1'

Related

SQL: Getting null values on comparing current values to previous period

I have copied this SQL query from another website and I'm not actually sure why it returns null values for the previous period and the month comparing it with. What should I adjust in the query to get the desired result?
SQL Query:
WITH monthly_metrics AS (
SELECT EXTRACT(year from day) as year,
EXTRACT(month from day) as month,
SUM(revenue) as revenue
FROM daily_metrics
GROUP BY 1,2
)
SELECT year AS current_year,
month AS current_month,
revenue AS revenue_current_month,
LAG(year,12) OVER ( ORDER BY year, month) AS previous_year,
LAG(month,12) OVER ( ORDER BY year, month) AS month_comparing_with,
LAG(revenue,12) OVER ( ORDER BY year, month) AS revenue_12_months_ago,
revenue - LAG(revenue,12) OVER (ORDER BY year, month) AS month_to_month_difference
FROM monthly_metrics
ORDER BY 1,2;
Query Result:
dbfiddle

MySQL Rolling Count of Rows

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.

Sum of Counts from successive dates

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,

getting multi row data into columns in mysql

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

Count totals by year and month

I have a table that looks like this:
id,created,action
1,'2011-01-01 04:28:21','signup'
2,'2011-01-05 04:28:21','signup'
3,'2011-02-02 04:28:21','signup'
How do I select and group these so the output is:
year,month,total
2011,1,2
2011,2,1
Try this:
SELECT DATE_FORMAT(created, '%Y') as 'year',
DATE_FORMAT(created, '%m') as 'month',
COUNT(id) as 'total'
FROM table_name
GROUP BY DATE_FORMAT(created, '%Y%m')
SELECT YEAR(created) as year_val, MONTH(created) as month_val ,COUNT(*) as total
FROM testing
GROUP BY YEAR(created), MONTH(created)