I'm going to do monthly COUNT.
I want to COUNT the data from now to 15 months ago.I want it to be marked 0 in the month when there is no data.
Here is My Table
PeopleFruit
Id
Fruit
People
CreatedAt
1
Apple
John
2020-11-13
2
Banana
Katie
2020-11-25
3
Kiwi
Sam
2021-03-03
4
Apple
Katie
2021-04-12
5
Apple
Katie
2021-04-24
6
Apple
John
2021-04-30
7
Banana
Sam
2021-09-02
8
Banana
Katie
2021-11-11
9
Apple
Sam
2021-12-12
10
Kiwi
John
2021-12-15
I want to know the fruit COUNT by month so that people don't overlap.
ex)If you look at Apple in April(Month 4), Katie chose it twice and John chose it once, but because Katie overlaps, COUNT became 2.
I used this query.
SELECT DATE_FORMAT(createdAt,'%y.%m') m, COUNT(DISTINCT(People)) as count
FROM PeopleFruit
WHERE createdAt > DATE_ADD(NOW(),INTERVAL - 15 MONTH)
AND Fruit = 'Apple'
GROUP BY m;
result is
m
count
20.11
1
21.04
2
21.12
1
But this is the result I want.
m
count
21.12
1
21.11
0
21.10
0
21.09
0
21.08
0
21.07
0
21.06
0
21.05
0
21.04
2
21.03
0
21.02
0
21.01
0
20.12
0
20.11
1
20.10
0
What should I do?
Since MySQL 8.0 you can use RECURSIVE CTE like:
WITH RECURSIVE months(m) AS (
SELECT DATE_FORMAT(NOW(),'%Y-%m-01') m
UNION ALL
SELECT DATE_SUB(m, INTERVAL 1 MONTH) m FROM months
WHERE m > DATE_SUB(NOW(), INTERVAL 15 MONTH)
) SELECT
DATE_FORMAT(months.m,'%y.%m') m,
COALESCE(count, 0) count
FROM months
NATURAL LEFT JOIN (
SELECT DATE_FORMAT(createdAt,'%Y-%m-01') m, COUNT(DISTINCT(People)) as count
FROM PeopleFruit
WHERE createdAt > DATE_ADD(NOW(),INTERVAL - 15 MONTH) AND Fruit = 'Apple'
GROUP BY m
) months_count ;
MySQL recursive CTE test
Related
i need to get the top touristCount in each month like January Zambia has 4 touristCount i need to select only Zambia for January and so on
user
`useri_id` | `username` | `email` | `nationality`
1 Joseph `` US
2 Abraham. `` UK
3 g.wood '' Zambia
4 Messi. '' France
5 Ronaldo. '' Namibia
6 Pogba. '' Holand.
bookings
booking_id | user_id | booking_date | tour_id
1 1 2022-01-01 1
2 1 2022-01-01 6
3 1 2022-05-01 2
4 3 2022-01-01 5
5 2 2022-04-01 5
6 2 2022-11-01 7
7 3 2022-12-01 2
8 6 2022-01-01 1
this is what i have tried
SELECT s.nationality AS Nationality,
COUNT(b.tourist_id) AS touristsCount,
MONTH(STR_TO_DATE(b.booked_date, '%d-%m-%Y')) AS `MonthNumber`
FROM bookings b, users s
WHERE s.user_id = b.tourist_id
AND YEAR(STR_TO_DATE(b.booked_date, '%d-%m-%Y')) = '2022'
GROUP BY Nationality,MonthNumber
order BY MonthNumber ASC
LIMIT 100
i need the results to be like
nationality | TouritIdCount | MonthNumber
US 2 01
UK 1 04
US 1 05
UK 1 11
ZAMBIA 1 12
Try this :
SELECT nationality, COUNT(booking_id) AS TouristIdCount, MONTH(booking_date) AS MonthNumber
FROM users u
JOIN bookings b ON u.user_id = b.user_id
WHERE YEAR(booking_date) = 2022
GROUP BY nationality, MonthNumber
ORDER BY TouristIdCount DESC, MonthNumber ASC
you can use
having COUNT(b.tourist_id) >= 2
You want to count bookings per month and tourist's nationality and then show only the top nationality (or nationalities) per month.
There are two very similar approaches:
Rank the nationalities' booking counts per month with RANK and only show the best ranked rows.
Select the top booking count per month and only show rows matching their top count.
The following query uses the second method. It shows one row per month and top booking nationality. Often there may be excatly one row for a month showing the one top booking nationality, but there may also be months where nationalities tie and share the same top booking count, in which case we see more than one row for a month.
select year, month, nationality, booking_count
from
(
select
year(b.booking_date) as year,
month(b.booking_date) as month,
u.nationality,
count(*) as booking_count,
max(count(*)) over (partition by year(b.booking_date), month(b.booking_date)) as months_max_booking_count
from bookings b
join users u on u.user_id = b.tourist_id
group by year(b.booking_date), month(b.booking_date), u.nationality
) ranked
where booking_count = months_max_booking_count
order by year, month, nationality;
As your own sample data doesn't contain any edge cases, here is some other sample data along with my query's result and an explanation. (In other words, this is what you should have shown in your request ideally.)
users
user_id
username
email
nationality
1
Joseph
joseph#mail.us
US
2
Mary
mary#mail.us
US
3
Abraham
abraham#mail.uk
UK
bookings
booking_id
user_id
booking_date
tour_id
1
1
2022-01-11
1
2
2
2022-01-11
1
3
3
2022-01-11
1
4
3
2022-01-22
2
5
1
2022-05-01
3
6
2
2022-05-01
3
7
1
2022-05-12
4
8
2
2022-05-12
4
9
3
2022-05-14
5
10
3
2022-05-20
6
11
3
2022-05-27
7
result
year
month
nationality
booking_count
2022
1
UK
2
2022
1
US
2
2022
5
US
4
In January there were two tours, but we are not interested in tours. We see four bookings, two by the Americans, two by the Britsh person. This is a tie, and we show two rows, one for UK and one for US with two bookings each.
In May there were five tours, but again, we are not interested in tours. There are seven bookings, four by the Americans, three by the Britsh person. So we only show US as the top country with four bookings here.
hi guys i really newbie in sql, i need help to generate percentage of attendance, here is the table:
Table Schedule
Schedule_ID Course_ID Lecture_ID Start_Date End_Date Course_Days
1 1 11 2019-09-09 2019-12-08 2,4,6
2 3 4 2019-09-09 2019-12-08 3,4
3 4 13 2019-09-09 2019-12-08 2,5
4 5 28 2019-09-09 2019-12-08 3
5 2 56 2020-01-27 2020-04-26 2,4
6 7 1 2020-01-27 2020-04-26 4,5
7 1 11 2020-01-27 2020-04-26 2,4,6
8 7 22 2020-01-27 2020-04-26 2,3
9 8 56 2020-01-27 2020-04-26 5
10 3 37 2020-01-27 2020-04-26 5,6
Reference of days of week used in this data.
1: Sunday, 2:Monday, 3:Tuesday, 4:Wednesday, 5:Thursday, 6:Friday, 7:Saturday
Table course_attendance
ID STUDENT_ID SCHEDULE_ID ATTEND_DT
1 1 2 2019-09-10
2 1 2 2019-09-11
3 1 2 2019-09-17
4 1 2 2019-09-18
......
46 2 1 2019-12-02
47 2 1 2019-09-11
48 2 1 2019-09-18
49 2 1 2019-09-25
50 2 1 2019-10-09
51 2 1 2019-10-16
....
111 6 1 2019-09-23
112 6 1 2019-09-30
113 6 1 2019-10-07
114 6 1 2019-10-14
table student
ID NAME
1 Jonny
2 Cecilia
3 Frank
4 Jones
5 Don
6 Harry
i need to show up like this :
STUDENT_ID NAME Course_ID Attendance rate
1 Jonny 1 82%
2 Cecilia 1 30%
3 Frank 3 100%
4 Jones 2 100%
5 Don 2 25%
6 Harry 4 40%
EDIT this my last step to get percentage:
result:
with main as (
select ca.STUDENT_ID,
ca.SCHEDULE_ID,
s.COURSE_ID,
co.NAME as course_name,
st.NAME,
count(ca.ID) as total_attendance,
((CHAR_LENGTH(s.COURSE_DAYS) - CHAR_LENGTH(REPLACE(s.COURSE_DAYS , ',', '')) + 1) * 13) as attendance_needed
from univ.course_attendance ca
left join univ.schedule s on ca.SCHEDULE_ID = s.ID
left join univ.student st on ca.SCHEDULE_ID = st.ID
left join univ.course co on ca.SCHEDULE_ID = co.ID
group by ca.STUDENT_ID, ca.SCHEDULE_ID
)
select *,total_attendance/attendance_needed as attendance_percentage
from main
order by 1,2;
This can be done following three steps.
Step 1: Calculate the total number of days a particular course of a schedule has. It's a good thing the start_date is always on Monday and the end_date is always on Sunday, which makes the week complete and saves some trouble. By calculating the total number of weeks a course go through and the number of days a week has for that course, we can get the total number of days a particular course of a schedule has.
Step 2:Calculate the total number of days a student for a schedule. This is done fairly easily. Note: As the majority part of the table has been skipped and the OP has yet to provide the complete data set, I could only have 14 existing rows provided.
Step 3: Calculate the percentage for the attendance using the result from the above two steps and get other required columns.
Here is the complete statement I wrote and tested in workbench:
select t2.student_id as student_id,`name`,course_id, (t2.total_attendance/t1.total_course_days)*100 as attendance_rate
from (select schedule_id,course_id,
length(replace(course_days,',',''))*(week(end_date)-week(start_date)) as total_course_days
from Schedule) t1
JOIN
(select count(attend_dt) as total_attendance,student_id,schedule_id
from course_attendance group by student_id, schedule_id) t2
ON t1.schedule_id=t2.schedule_id
JOIN
student s
ON t2.student_id=s.id;
Here is the result set ( the attendance_rate is not nice due to the abridged course_attendance table):
student_id, name, course_id, attendance_rate
2, Cecilia, 1, 15.3846
6, Harry, 1, 10.2564
1, Jonny, 3, 15.3846
I have 1 table named ItemDelivery. I wanted to get the count of items that has DeliveryDate and the items that has been receivedDate per month. Some items deliveryDate month have different receiveDate month such as items scheduled for delivery on the later part of the month would be received on early days of succeeding month. Some may take months to be delivered for overseas.
This is the data:
id iditem deliveryDate receiveDate
1 2 2021-01-03 2021-01-05
2 2 2021-01-03
3 3 2021-02-05 2021-02-06
4 5 2021-02-05
5 4 2021-02-20 2021-03-01
6 3 2021-03-15 2021-04-08
I would like to have
Mo Delivery Recieve
Jan 2 1
Feb 3 1
Mar 1 1
Apr 0 1
This query gives 1 columns only
select date_format(deliveryDate,'%b') as mo ,
count(id) as delivery
from ItemDelivery
where year(deliveryDate)=2021
group by month(deliveryDate)
union all
select date_format(receiveDate,'%b') as mo ,
count(id) as received
from ItemDelivery
where year(receiveDate)=2021
group by month(receiveDate)
Output:
Mo Delivery
Jan 2
Feb 3
Mar 1
Jan 1
Feb 1
Mar 1
Apr 1
This query also have different output
SELECT d1.mo, d1.delivery, d2.received
FROM
(SELECT month(deliveryDate) as mo, count(id) AS delivery
FROM ItemDelivery
WHERE year(deliveryDate)=2021 group by month(deliveryDate)) as d1,
(SELECT month(receiveDate) as mo, count(id) AS received
FROM ItemDelivery
WHERE year(receiveDate)=2021 group by month(receiveDate)) as d2
Output:
mo delivery received
1 2 1
2 3 1
3 1 1
1 2 1
2 3 1
3 1 1
1 2 1
2 3 1
3 1 1
1 2 1
2 3 1
3 1 1
This has also the same output except if I use condition d1.mo=d2.mo:
select d1.mo, d1.delivery, d2.received
from
(SELECT month(deliveryDate) as mo, count(id) as delivery
FROM ItemDelivery
WHERE year(deliveryDate)=2021 group by month(deliveryDate)) d1
inner join
(SELECT month(receiveDate) as mo, count(id) as received
FROM ItemDelivery
WHERE year(receiveDate)=2021 group by month(receiveDate)) d2
Any suggestions ?
SELECT
date_format(eventDate,'%b') AS mo,
SUM(delivery) AS delivery,
SUM(receive) AS receive
FROM
(
SELECT deliveryDate AS eventDate, 1 AS delivery, 0 AS receive FROM ItemDelivery
UNION ALL
SELECT receiveDate AS eventDate, 0 AS delivery, 1 AS receive FROM ItemDelivery
)
AS rotated
WHERE
eventDate >= '2021-01-01'
AND eventDate < '2022-01-01'
GROUP BY
month(eventDate)
My table votes contains votes that have been made by users at different times:
id item_id position user_id created_at
1 2 0 1 11/21/2013 11:27
26 1 1 1 11/21/2013 11:27
27 3 2 1 11/21/2013 11:27
42 2 2 1 12/7/2013 2:20
41 3 1 1 12/7/2013 2:20
40 1 0 1 12/7/2013 2:20
67 2 2 1 12/13/2013 1:13
68 1 1 1 12/13/2013 1:13
69 3 0 1 12/13/2013 1:13
84 2 0 1 12/28/2013 2:29
83 3 2 1 12/28/2013 2:29
82 1 1 1 12/28/2013 2:29
113 3 0 1 1/17/2014 22:08
114 1 1 1 1/17/2014 22:08
115 2 2 1 1/17/2014 22:08
138 2 0 1 1/20/2014 16:49
139 1 1 1 1/20/2014 16:49
140 3 2 1 1/20/2014 16:49
141 1 1 11 1/20/2014 16:51
142 3 2 11 1/20/2014 16:51
143 2 0 11 1/20/2014 16:51
I need to tally the results on a monthly basis but here's the tricky part: the start/end of the month does not necessarily fall on the first day of the month. So if the votes are due on the 10th day of every month, I need a vote that was cast on the 10th to be in a different group from a vote that was cast on the 11th. Using the data above, I want to get three groups:
Group 1: 6 votes (11/21 and 12/7)
Group 2: 6 votes (12/13, 12/28)
Group 3: 9 votes (1/17, 1/20)
I've tried a lot of approaches but to no avail. This is my query right now:
select created_at, ADDDATE(DATE_FORMAT(created_at, '%Y-%m-01'),interval 10 day) as duedate,count("id") from votes where list_id = 2 group by duedate
I am getting group sizes of 3, 9, and 9, not 6, 6 and 9. Any help you can provide would be much appreciated. Thanks.
Your query is close. You just need to subtract 9 days (10 - 1) from the current day to get the month:
select created_at, date_sub(created_at, interval 9 day) as duedate,
count(id)
from votes
where list_id = 2
group by duedate;
date_format() converts a date to a string. There is no need to convert a date value to a character value for this query.
EDIT:
To group by month:
select date_format(date_sub(created_at, interval 9 day), '%Y-%m') as YYYYMM,
count(id)
from votes
where list_id = 2
group by YYYYMM;
I've 4 tables as shown below
doctors
id name
------------
1 Mathew
2 Praveen
3 Rosie
4 Arjun
5 Denis
doctors_appointments
id doctors_id patient_name contact date status
--------------------------------------------------------------------------------------
1 5 Nidhin 9876543210 2012-12-10 15:39:41 Registered
2 5 Sunny 9876543210 2012-12-18 15:39:48 Registered
3 5 Mani 9876543210 2012-12-12 15:39:57 Registered
4 2 John 9876543210 2012-12-24 15:40:09 Registered
5 4 Raj 9876543210 2012-12-05 15:41:57 Registered
6 3 Samuel 9876543210 2012-12-14 15:41:33 Registered
7 2 Louis 9876543210 2012-12-24 15:40:23 Registered
8 1 Federick 9876543210 2012-12-28 15:41:05 Registered
9 2 Sam 9876543210 2012-12-12 15:40:38 Registered
10 4 Sita 9876543210 2012-12-12 15:41:00 Registered
doctors_dutyplan
id doctor_id weeks time no_of_patients
------------------------------------------------------------------
1 1 3,6,7 9:00am-1:00pm 10
2 2 3,4,5 1:00pm-4:00pm 7
3 3 3,6,7 10:00am-2:00pm 10
4 4 3,4,5,6 8:30am-12:30pm 12
5 5 3,4,5,6,7 9:00am-4:00pm 30
emp_leave
id empid leavedate
--------------------------------
1 2 2012-12-05 14:42:36
2 2 2012-12-03 14:42:59
3 3 2012-12-03 14:43:06
4 3 2012-12-06 14:43:14
5 5 2012-12-04 14:43:24
My task is to find all the days in a month in which the doctor is available excluding the leave dates.
My query what is wrote is given below:
SELECT DATE_ADD( '2012-12-01', INTERVAL
ROW DAY ) AS Date,
ROW +1 AS DayOfMonth
FROM (
SELECT #row := #row +1 AS
ROW FROM (
SELECT 0
UNION ALL SELECT 1
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
)t1, (
SELECT 0
UNION ALL SELECT 1
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
UNION ALL SELECT 6
)t2, (
SELECT #row := -1
)t3
LIMIT 31
)b
WHERE DATE_ADD( '2012-12-01', INTERVAL
ROW DAY )
BETWEEN '2012-12-01'
AND '2012-12-31'
AND DAYOFWEEK( DATE_ADD( '2012-12-01', INTERVAL
ROW DAY ) ) =2
AND DATE_ADD( '2012-12-01', INTERVAL
ROW DAY ) NOT
IN (
SELECT DATE_FORMAT( l.leavedate, '%Y-%m-%d' ) AS date
FROM doctors_dutyplan d
LEFT JOIN emp_leave AS l ON d.doctor_id = l.empid
WHERE doctor_id =2
)
This works fine for all doctors who took any leave in a particular day in a month (here in the example it is Decemeber 2012). and the result for the above query is shown below:
Date DayOfMonth
-----------------------
2012-12-10 10
2012-12-17 17
2012-12-24 24
2012-12-31 31
But on the other hand for the doctors who didn't took any leave , for that my query is showing empty table, example for the doctor Mathew whose id is 1, my query returns an empty result
can anyone please tell a solution for this problem.
Thanks in advance.
Your query is large, but this part looks fishy:
NOT IN (
SELECT DATE_FORMAT( l.leavedate, '%Y-%m-%d' ) AS date
FROM doctors_dutyplan d
LEFT JOIN emp_leave AS l ON d.doctor_id = l.empid
WHERE doctor_id =2
The left join means a null would be returned for doctor 1. Now, col1 not in (null) does not behave as you may expect. It translates to:
col1 <> null
Which is never true. You could solve this by changing the left join to an inner join, so an empty set instead of null is returned for a doctor without leave.