I want to list grouped by month and the days remaining to complete the course in the next column. The Course has 10 days.
Example data
ID Name Date
1 Sandy 2015-05-06
2 Candy 2015-05-06
3 Sandy 2015-05-28
4 Candy 2015-05-29
5 Candy 2015-06-01
Preferred output
| Name | Month | Attended | Remaining|
| Sandy| May | 2 | 8 |
| Candy| May | 2 | 8 |
| Candy| June | 1 | 7 |
If I use GROUP BY DATE_FORMAT(date, '%Y%m'), Name and try to do the calculation it does not work.
You need two different aggregates:
The number of days attended in the current month for a given user.
The number of days attended in all months up to and including the current month for a given user.
That's a tad fiddly, so 'tis time to do some Test-Driven Query Design (TDQD).
The table in the question is anonymous — that's such a common and irritating situation. So, the table is henceforth CourseAttendance, with the three columns shown in the data (ID, Name, Date).
Number of days attended by user in a specific month
Assuming the expression DATE_FORMAT(date, '%Y-%m') is syntactically valid, and that neither Date nor Month as a column name causes problems, then:
SELECT DATE_FORMAT(Date, '%Y-%m') AS Month,
Name,
COUNT(*) AS NumDays
FROM CourseAttendance
GROUP BY Month, Name
This should produce:
Month Name NumDays
2015-05 Sandy 2
2015-05 Candy 2
2015-06 Candy 1
Number of days attended by user up to and including a specific month
This time, the aggregate has to be over all dates less than or equal to the converted month value:
SELECT D.Month, D.Name, SUM(C.NumDays) AS TotDays
FROM (SELECT DISTINCT DATE_FORMAT(Date, '%Y-%m') AS Month, Name
FROM CourseAttendance
) AS D
JOIN (SELECT DATE_FORMAT(Date, '%Y-%m') AS Month,
Name,
COUNT(*) AS NumDays
FROM CourseAttendance
GROUP BY Month, Name
) AS C
ON C.Month <= D.Month AND C.Name = D.Name
GROUP BY D.Month, D.Name
This should give the output:
Month Name NumDays
2015-05 Sandy 2
2015-05 Candy 2
2015-06 Candy 3
Assembling the final result
The two previous result tables need to be joined on Month and Name, to yield the result:
SELECT A.Name, A.Month, A.NumDays AS Attended, (10 - B.TotDays) AS Remaining
FROM (SELECT DATE_FORMAT(Date, '%Y-%m') AS Month,
Name,
COUNT(*) AS NumDays
FROM CourseAttendance
GROUP BY Month, Name
) AS A
JOIN (SELECT D.Month, D.Name, SUM(C.NumDays) AS TotDays
FROM (SELECT DISTINCT DATE_FORMAT(Date, '%Y-%m') AS Month, Name
FROM CourseAttendance
) AS D
JOIN (SELECT DATE_FORMAT(Date, '%Y-%m') AS Month,
Name,
COUNT(*) AS NumDays
FROM CourseAttendance
GROUP BY Month, Name
) AS C
ON C.Month <= D.Month AND C.Name = D.Name
GROUP BY D.Month, D.Name
) AS B
ON A.Month = B.Month AND A.Name = B.Name
ORDER BY A.Name, A.Month
This should give output like:
Name Month Attended Remaining
Candy 2015-05 2 8
Candy 2015-06 1 7
Sandy 2015-05 2 8
You can fettle the month value into a month name if you need to. You can also fettle the sort order if you want month and name within month, etc.
Related
I'm loking for one logic that might be not accepatable.
But my requirement is I want count of customers(NewCustomers, repeatCustomers) on the basis of previous and current month
Like from this data I want
DATE NAME
2016-01-01 A
2016-01-01 B
2016-01-01 C
2016-01-05 E
2016-01-05 F
2016-01-25 G
2016-01-25 H
2016-02-25 A
2016-02-25 E
2016-02-10 X
2016-02-11 Y
2016-02-13 F
Output like this
MONTH NewCustomer RepeatCustomer CustomerCount of refernece month (Like here is JAN)
FEB 2 3 7
Same will go for next months
Any suggestion ? Thanks !!
I don't know what the reference month is, but you can get the first two columns by combining the first time you see a customer with who visits in each month:
select date_format(c.date, '%Y-%m') as yyyymm,
count(distinct c.name) as NumCustomers,
sum(case when date_format(c.date, '%Y-%m') <> date_format(cc.start_date, '%Y-%m')
then 1 else 0
end) as NumRepeatCustomers
from customers c join
(select c.name, min(c.date) as start_date
from customers c
group by c.name
) cc
on c.name = cc.name
group by date_format(c.date, '%Y-%m')
order by yyyymm;
Here's my table, showing user names and the timestamp they scored a point:
id user date
1 Aaron 23/02/2012 22:44
2 Betty 23/02/2012 22:47
3 Carlos 24/02/2012 16:01
4 David 28/02/2012 11:40
5 David 28/02/2012 12:32
6 David 28/02/2012 16:59
7 Aaron 2/03/2012 13:46
8 Aaron 30/03/2012 18:37
9 Betty 30/03/2012 19:58
10 Emma 9/04/2012 6:49
11 Emma 9/04/2012 13:19
12 Emma 9/04/2012 18:20
13 Emma 9/04/2012 20:46
14 Aaron 10/04/2012 15:47
15 Betty 10/04/2012 19:15
16 Betty 10/04/2012 20:40
17 Carlos 11/04/2012 9:44
18 Carlos 11/04/2012 20:01
19 David 11/04/2012 23:17
20 David 12/04/2012 17:09
And here is the results table I am trying to achieve, i.e. an x axis showing month-year, and a y axis displaying the number of users who reached a certain points threshold within that month:
date 1 point First time? 2 points First time? 3 points First time? 4 points First time? Total
Feb-12 A,B,C A,B,C D D 4
Mar-12 B A A 3
Apr-12 A,B,C B,C,D B,C,D E E 4
I've only got as far as calculating the total number of points and the total number of distinct scorers within a given month:
SELECT DISTINCT CONCAT (MONTHNAME(date), ' ', YEAR(date)) as 'date', COUNT(id) as total_points, COUNT(distinct referrer_id) as number_of_scorers
from points
group by CONCAT (MONTH(date), ' ', YEAR(date))
order by YEAR(date), MONTH(date)
which is only giving me:
date total_points number_of_scorers
Feb-12 6 4
Mar-12 3 3
etc.
So my questions are:
How can I amend the query to show me which users reached each point threshold within each month?
How can I amend the query to show me which users reached each point threshold for the first time within that month?
Thanks
The basic query you need is this:
select date_format(date, '%Y-%m') as yyyymm, user, count(*) as points
from t
group by date_format(date, '%Y-%m') as yyyymm, user;
This gets the number of points for each user in a month.
The rest is just aggregations, joins, and conditions:
select ymu.yyyymm,
group_concat(case when ymu.points = 1 then user end) as Points1_Users,
group_concat(case when ymu.points = 1 and ymu.yyyymm = u.min_yyyymm then user end) as Points1_Users_First,
group_concat(case when ymu.points = 2 then user end) as Points2_Users,
group_concat(case when ymu.points = 2 and ymu.yyyymm = u.min_yyyymm then user end) as Points2_Users_First
from (select date_format(date, '%Y-%m') as yyyymm, user, count(*) as points
from t
group by date_format(date, '%Y-%m') as yyyymm, user
) ymu join
(select user, min(yyyymm) as min_yyyymm
from (select date_format(date, '%Y-%m') as yyyymm, user, count(*) as points
from t
group by date_format(date, '%Y-%m') as yyyymm, user
) t
group by user
) u
on ymu.user = u.user
group by yyyymm
order by yyyymm;
I have an example table like this:
Month City Person
8 LHR ABC
10 BEIJING BCS
11 NY JJJ
11 VENICE hghg
11 VENICE KKK
12 NY aa
12 ORL abc
12 ORL bbc
So what I want to achieve is see the city in a specific month with the most number of people visiting
Like the output should be:
12 ORL
11 VENINCE
10 BEIJING
8 LHR
I have tried grouping it like
SELECT month, city , count(*) AS 'no of people visiting'
FROM table
GROUP BY month, city
This table does tell me which city and month location had how many people
visiting but I cannot extract the the top most month and city combination
with respect to a certain month.
Updated Query (with error)
SELECT *
FROM
( SELECT monthname(reservation.PickupDate), location.LocationName, COUNT(*) AS count
FROM reservation NATURAL JOIN location
WHERE reservation.pickupdate >= DATE_ADD(NOW(), INTERVAL - 3 MONTH)
GROUP BY month(reservation.PickupDate), location.LocationName) AS t1
WHERE NOT EXISTS (SELECT 1
FROM reservation R1 NATURAL JOIN location L1
WHERE monthname(R1.PickupDate) = monthname(t1.PickupDate)
GROUP BY month(R1.PickupDate), L1.LocationName)
Starting from your query, you just need to eliminate those rows having another city with more visitors on that month:
SELECT *
FROM
(SELECT `month`, city, count(*) AS cnt
FROM `table`
GROUP BY `month`, city) t1
WHERE NOT EXISTS(SELECT 1
FROM `table` t2
WHERE t2.`month` = t1.`month`
GROUP BY `month`, city
HAVING count(*) > t1.cnt)
I have a table something like this:
id | Customer | date
-----------------------------------------
1 | Customer2 | 2013-08-01 00:00:00
-----------------------------------------
2 | Customer1 | 2013-07-15 00:00:00
-----------------------------------------
3 | Customer1 | 2013-07-01 00:00:00
-----------------------------------------
. | ... | ...
-----------------------------------------
n | CustomerN | 2012-03-01 00:00:00
I want to calculate the "gained" customers for each month, the "lost" customers for each month and the Net Gain for each month, even if done in separate tables / views.
How can I do that?
EDIT
Ok, let me demonstrate what I've done so far.
To select Gained customers for any month, I've tried to select customers from Bookings table where the following not exist:
select Customer
from Bookings
where not exists
(select Customer
from Bookings
where
(Bookings.date BETWEEN
DATE_FORMAT(DATE_SUB(Bookings.date, INTERVAL 1 MONTH), '%Y-%m-01 00:00:00')
AND DATE_FORMAT(Bookings.date, '%Y-%m-01 00:00:00'
)
) AND Bookings.date >= STR_TO_DATE('2010-11-01 00:00:00', '%Y-%m-%d 00:00:00'))
This supposedly gets the customers that existed in the "selected" month but not in the previous one. "2010-11-01" is the date of the start of bookings + 1 month.
To select Lost customers for any month, I've tried to select customers from Bookings table where the following not exist:
select Customer
from Booking
where not exists
(select Customer
from Bookings
where
(Bookings.date BETWEEN
DATE_FORMAT(Bookings.date, '%Y-%m-01 00:00:00')
AND Bookings.date
)
AND Bookings.date >= STR_TO_DATE('2010-11-01 00:00:00', '%Y-%m-%d 00:00:00'
)
)
This supposedly gets the customers that existed in a previous month but not in the "selected" one.
For the "Loss" SQL query I got empty result! For the "Gain" I got thousands of rows but not sure if that's accurate.
You can use COUNT DISTINCT to count your customers, and WHERE YEAR(Date) = [year] AND MONTH(Date) = [month] to get the month.
The total number of customers in Sept 2013:
SELECT COUNT(DISTINCT Customer) AS MonthTotalCustomers FROM table
WHERE YEAR(date) = 2013 AND MONTH(date) = 9
The customers gained in Sept 2013:
SELECT COUNT(DISTINCT Customer) AS MonthGainedCustomers FROM table
WHERE YEAR(date) = 2013 AND MONTH(date) = 9
AND Customer NOT IN
(SELECT Customer FROM table
WHERE date < '2013-09-01')
Figuring out the lost customers is more difficult. I would need to know by what criteria you consider them to be 'lost.' If you just mean that they were around in August 2013 but they were not around in September 2013:
SELECT COUNT(DISTINCT Customer) AS MonthLostCustomers FROM table
WHERE YEAR(date) = 2013 AND MONTH(date) = 8
AND Customer NOT IN
(SELECT Customer FROM table
WHERE YEAR(date) = 2013 AND MONTH(date) = 9)
I hope from these examples you can extrapolate what you're looking for.
i have two tables, say Costs and Payments, and i need to calculate the AVG of monthly payments for each costs.
i.e. Costs_IdCost = 2 has three payments
+-----------+--------------+----------+--------+
| IdPayment | Costs_IdCost | Date | Amount |
+-----------+--------------+----------+--------+
| 1 | 2 |2012/09/10| 1000 |
+-----------+--------------+----------+--------+
| 2 | 2 |2012/09/20| 3000 |
+-----------+--------------+----------+--------+
| 3 | 2 |2012/10/01| 5000 |
+-----------+--------------+----------+--------+
now i need not just the average of payments (3000) but the average between:
September : (1000+3000)/2 = 2000
October : 5000 /1 = 5000
AVG : (2000+5000)/2 = 3500
and i'm getting pretty messy with group by and subquery >_<
thank you!
--------------------EDIT---------------------------
i'm using mySql, this is what i did so far:
SELECT c.IdCost,
c.Name,
c.Amount,
AVG(p.monthly_sum) Mean,
SUM( p.Amount ) Total,
COUNT( p.IdPayment ) Num
FROM Costs c
LEFT JOIN (SELECT MONTH(Date),
YEAR(Date),
Costs_IdCost,
IdPayment,
Amount,
AVG (Amount) monthly_sum
FROM Payments
GROUP BY YEAR(Date), MONTH(Date)
) p ON p.Costs_IdCost = c.IdCost
GROUP BY c.IdCost
I am not sure if this is exactly what you are looking for but it will give you the desired results:
select date_format(date, '%Y-%m') Month,
avg(amount) AvgAmount
from costs c
left join payments p
on c.id = p.costs_idcost
group by date_format(date, '%Y-%m')
union all
select concat(cast(count(*) as char), ' Months Averaged'),
avg(AvgAmount) TotalAvg
from
(
select avg(amount) AvgAmount,
date_format(date, '%Y-%m') Month
from costs c
left join payments p
on c.id = p.costs_idcost
group by date_format(date, '%Y-%m')
) x
See SQL Fiddle with Demo
You can group by month part of date.
GROUP BY datepart(MONTH, Cost.Date)
This may not the most efficient way, but it might work nicely. BTW datepart is build in function of MSSQL.
I do similar by using a select entry of
SELECT format(DATE_TIME,''yyyy-MM'') as [Month], .......
GROUP BY format(DATE_TIME,''yyyy-MM''), .........
The SORT BY is the same as what GROUP BY I am using. Not that one can even got to the hour, minute and second level with this concept if you want
example: SELECT format(DATE_TIME,''yyyy-MM-dd.HH'') as [Hour]