MYSQL : Grouping rows by dates and making fields in to column names - mysql

Have have table like this
id date user call_count
1 2020-09-15 Tim 4
2 2020-09-14 Tim 6
3 2020-09-04 Jamie 1
4 2020-09-02 Tim 2
5 2020-09-07 Tim 5
6 2020-09-01 Jamie 1
7 2020-09-01 Tim 5
8 2020-09-10 Jamie 4
9 2020-09-12 Tim 5
10 2020-09-22 Tim 44
11 2020-09-22 Tony 32
I'd like to have a single query that groups by date, putting each user as the name of a column and displaying their count for that date. Right now I am accomplishing this by a few loops of selects, but it is ugly and not efficient.
Result should look like
Date Tim Tony Jamie
2020-09-22 44 32
2020-09-15 4
2020-09-14 6
2020-09-12 5
2020-09-10 4
2020-09-07 5
2020-09-04 1
2020-09-02 2
2020-09-01 5 1
I've searched quite a bit for something like this, but not finding much. Thank you

Use conditional aggregation:
SELECT
Date,
SUM(CASE WHEN user = 'Tim' THEN call_count END) AS Tim,
SUM(CASE WHEN user = 'Tony' THEN call_count END) AS Tony,
SUM(CASE WHEN user = 'Jamie' THEN call_count END) AS Jamie
FROM yourTable
GROUP BY
Date
ORDER BY
Date;

Related

how to generate student attendance percentage per course, when they have specific day in a week

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

MYSQL I am trying to return a value where I need to compare one value against the minimum value for the same field when grouped against another field

Basically I am trying to calculate shots received in golf for various four balls, here is my data:-
DatePlayed PlayerID HCap Groups Hole01 Hole02 Hole03 Shots
----------------------------------------------------------------------
2018-11-10 001 15 2 7 3 6
2018-11-10 004 20 1 7 4 6
2018-11-10 025 20 2 7 4 5
2018-11-10 047 17 1 8 3 6
2018-11-10 048 20 2 8 4 6
2018-11-10 056 17 1 6 3 5
2018-11-10 087 18 1 7 3 5
I want to retrieve the above lines with an additional column which is to be calculated depending on the value in the group column, which is the players (Handicap - (the lowest handicap in the group)) x .75
I can achieve it in a group by but need to aggregate everything, is there a way I can return the value as above?, here is query that returns the value:
SELECT
PlayerID,
MIN(Handicap),
MIN(Hole01) AS Hole01,
MIN(Hole02) AS Hole02,
MIN(Hole03) AS Hole03,
MIN(CourseID) AS CourseID,
Groups,
ROUND(
MIN((Handicap -
(SELECT MIN(Handicap) FROM Results AS t
WHERE DatePlayed='2018-11-10 00:00:00' AND t.Groups=Results.Groups)) *.75))
AS Shots
FROM
Results
WHERE
Results.DatePlayed='2018=11=10 00:00:00'
GROUP BY
DatePlayed, Groups, PlayerID
.
PlayerID MIN(Handicap)Hole01 Hole02 Hole03 CourseID Groups Shots
-----------------------------------------------------------------
4 20 7 4 6 1 1 2
47 17 8 3 6 1 1 0
56 17 6 3 5 1 1 0
87 18 7 3 5 1 1 1
1 15 7 3 6 1 2 0
25 20 7 4 5 1 2 4
48 20 8 4 6 1 2 4
Sorry about any formatting really couldn't see how to get my table in here, any help will be much appreciated, I am using the latest mysql from ubuntu 18.04
Not an answer; too long for a comment...
First off, I happily know nothing about golf, so what follows might not be optimal, but it must, at least, be a step in the right direction...
A normalized schema might look something like this...
rounds
round_id DatePlayed PlayerID HCap Groups
1 2018-11-10 1 15 2
2 2018-11-10 4 20 1
round_detail
round_id hole shots
1 1 7
1 2 3
1 3 6
2 1 7
2 2 4
2 3 6
Hi Guys I have found the solution, basically I need to drop the MIN immediately after the ROUND of the equation and therefore it does not need a Group By.
SELECT
PlayerID,
Handicap,
Hole01,
Hole02,
Hole03,
CourseID,
Groups,
ROUND((Handicap -
(SELECT MIN(Handicap) FROM Results AS t
WHERE DatePlayed='2018-11-10 00:00:00'
AND t.Groups=Results.Groups))
*.75) AS Shots
FROM
Results
WHERE
Results.DatePlayed='2018=11=10 00:00:00'

Show entries for every item even if record doesn't exist in MySQL

I have two tables. The first table is a list of lots - table name lot. There are a total of 3 lots that are managed by three people and the fields are: lot.lotID, lot.LotName
Every day, these lots will collect eggs, but sometimes, the people collecting will forget to make the entry at the end of the day. These entries go into a table called deposit. This table has deposit.DepID, deposit.DepDate, deposit.LotID and deposit.DepAmount
I need to list every lot each day and get each deposit amount. If there is none made, instead of not displaying a record, it should show that the record is a NULL value or 0. For instance if no entry is inserted into the database for lot 2 on 2018-10-11, I should see this if I query two days' worth of data:
LotID | LotName | Date | DepAmount
1 | Sarah | 2018-10-09 | 67
2 | Dave | 2018-10-09 | 84
3 | Mike | 2018-10-09 | 78
1 | Sarah | 2018-10-10 | 100
2 | Dave | 2018-10-10 | 0
3 | Mike | 2018-10-10 | 49
Alternately, it is okay if lot 2 on 10-10 says:
2 | Dave | 2018-10-10 | {null}
Can someone give me a bit of direction here? It seems like it would be insanely simple, but I can't find much on the subject. Thanks in advance!!
PS - Here are the two tables:
lot:
LotID Name
1 Jim
2 Mary
3 Jeff
4 Steve
5 Clinton
6 George
7 Jennifer
and deposit:
DepDate DepAmount UserID LotID
2018-10-09 07:09:13 150.00 1
2018-10-09 07:21:22 345.00 2
2018-10-09 19:18:33 283.00 3
2018-10-09 19:37:51 100.00 4
2018-10-09 14:11:47 357.00 8 5
2018-10-09 14:21:43 5324.00 8 6
2018-10-09 14:27:46 564.50 8 7
2018-10-10 14:32:29 3543.75 6 2
2018-10-10 23:12:40 234.00 10 3
2018-10-10 07:09:13 52.00 1
2018-10-11 07:09:13 234.00 3
2018-10-10 07:09:13 764.00 4
2018-10-10 07:09:13 123.00 6
2018-10-10 07:09:13 764.00 7
This is a tricky problem as a straight LEFT JOIN will not work due to a person having values for dates other than the one which has no value in deposit. So we need to first CROSS JOIN lot with a list of all the dates in deposit to get a set of all people and all dates i.e.
SELECT l.LotId, l.LotName, dt.Date
FROM lot l
CROSS JOIN (SELECT DISTINCT DepDate AS Date
FROM deposit) dt
Output:
LotId LotName Date
1 Sarah 2018-10-09
2 Dave 2018-10-09
3 Mike 2018-10-09
1 Sarah 2018-10-10
2 Dave 2018-10-10
3 Mike 2018-10-10
Then we can LEFT JOIN this result with the deposit table to get our list of deposits for each day, using COALESCE to convert NULL values to 0:
SELECT l.LotId, l.LotName, dt.Date, COALESCE(d.DepAmount, 0) AS DepAmount
FROM lot l
CROSS JOIN (SELECT DISTINCT DepDate AS Date
FROM deposit) dt
LEFT JOIN deposit d ON d.LotId = l.lotId AND d.DepDate = dt.Date
ORDER BY Date, l.LotId
Output:
LotId LotName Date DepAmount
1 Sarah 2018-10-09 67
2 Dave 2018-10-09 84
3 Mike 2018-10-09 78
1 Sarah 2018-10-10 100
2 Dave 2018-10-10 0
3 Mike 2018-10-10 49

Query to get values from table based on criteria from same table, Mysql

I have a MySQL table with the following table structure and desired output
historical_id grd_id register_type timestamp address value historical_type insertion_time
5358 2 11 2016-05-07 12:45:00 1 18.1 1 2016-05-07 13:44:58
5359 2 11 2016-05-07 12:45:00 2 51.4 1 2016-05-07 13:44:58
5360 2 11 2016-05-07 12:45:00 3 476 1 2016-05-07 13:44:58
5364 2 11 2016-05-07 13:00:00 1 18.79 1 2016-05-07 13:59:58
5365 2 11 2016-05-07 13:00:00 2 51.2 1 2016-05-07 13:59:58
5366 2 11 2016-05-07 13:00:00 3 718 1 2016-05-07 13:59:58
Desired output from query
kWh_date temp rh co2
2016-05-0712:45:00 18.1 51.4 476
2016-05-0713:00:00 18.79 51.2 718
Can anybody help as to how I get the desired output. I have tried to 'GROUP' by timestamp (not my decision to use this name and cannot change it) but it just shows me 1 set of data (ie 'address' 1). I have used 3 'SELECT' statements but that shows the timestamp repeated. Any help is much appreciated.
I have tried
SELECT `register_type`
, `timestamp`
, `address`
, `value`
FROM grdxf.historical
WHERE `register_type` = 11
GROUP
BY `timestamp`;
I have tried different queries but they end up failing also. I'm really at a lose and new to queries.
E.g.:
SELECT FROM_UNIXTIME(900 * FLOOR(UNIX_TIMESTAMP(timestamp)/900)) timestamp
, MAX(CASE WHEN address = 1 THEN value END) temp
, MAX(CASE WHEN address = 2 THEN value END) rh
, MAX(CASE WHEN address = 3 THEN value END) co2
FROM historical
WHERE register_type = 11
GROUP
BY timestamp;

MySQL sum column and display last row in column

I have a table of users,subscription packages and various user subscriptions.
I need to fetch a sum of all subscription cost and display the latest/last subscription. The latest subscription is the subscription
with the highest subscription_id. How can I write my query? My tables are listed as below.
Users table
user_id name
1 John
2 Jane
3 Matthew
Subscription Packages table
package_id package_name
1 Basic
2 Advanced
3 Premium
User Subscriptions
subscription_id user_id package_id subscription_cost date
1 1 1 2 2014-04-01
2 2 1 2 2014-04-01
3 3 1 2 2014-04-01
4 1 1 2 2014-05-01
5 1 2 3.5 2014-06-01
6 2 2 3.5 2014-06-01
7 2 2 3.5 2014-07-01
8 1 3 5 2014-07-01
9 3 2 5 2014-07-01
10 2 2 3.5 2014-08-01
11 1 1 2 2014-08-01
My results should be like so
name total_costs latest_package
John 14.5 Basic
Jane 12.5 Advanced
Matthew 7 Premium
Because you need to do an aggregation anyway, I would go for the group_concat()/substring_index() trick:
select u.user_id, u.name, sum(subscription_cost) as total_costs,
substring_index(group_concat(p.package_name order by us.date desc), ',', 1) as latest_package
from usersubscriptions us join
users u
on us.user_id = u.user_id join
packages p
on us.package_id = p.package_id
group by u.user_id;
This assumes that no package names have commas. It is also subject to default limits on the length of the result of group_concat() -- but this method often works in practice.