Join a report table based on three relational tables - mysql

Below are the given tables:
`student` `subject`
------------------- -------------------
id name id subject
------------------- -------------------
1 Alice 1 Maths
2 Bob 2 Science
3 Eve 3 Economics
------------------- -------------------
`marks`
-----------------------------------------------------
id student_id subject_id marks
-----------------------------------------------------
1 1 1 30
2 1 2 40
3 2 3 50
4 3 1 60
5 3 2 70
-----------------------------------------------------
I need an output which should look like below:
`o/p`
----------------------------------------------
name subject marks
----------------------------------------------
Alice Maths 30
Alice Science 10
Alice Economics NULL
Bob Maths NULL
Bob Science NULL
Bob Economics 50
Eve Maths 60
Eve Science 70
Eve Economics NULL
----------------------------------------------
Please note that I am targeting MySQL 5.6.x syntax and I have created a SQL fiddle of above here to ease access to the question.

Here you go
select
st.name,
su.subject,
m.marks
from student as st
cross join subject as su
left join marks as m on m.student_id=st.id and m.subject_id=su.id
order by st.name, su.subject

SELECT student.name,
subject.subject,
marks.marks
FROM student
JOIN subject ON student.id = subject.id
JOIN marks ON student.id = marks.id
ORDER BY student.name,
subject.subject

Related

Find what genre is most frequent in each age category

I am thinking about making age groups per decade and find out what genre is more frequent. It is more difficult than I expected but here is what I have tried:
One table is like this, called: sell_log
id id_film id_cust
1 2 2
2 3 4
3 1 5
4 4 3
5 5 1
6 2 4
7 2 3
8 3 1
9 5 3
2nd here is a table about the films that has the id and the genres:
id_film genres
1 comedy
2 fantasy
3 sci-fi
4 drama
5 thriller
and 3rd table, customers is this:
id_cust date_of_birth_cust
1 1992-03-12
2 1999-06-25
3 1986-01-14
4 1985-09-18
5 1992-05-19
This is the code I did:
select id_cust,date_of_birth_cust,
CASE
WHEN date_of_birth_cust > 1980-01-01 and date_of_birth_cust < 1990-01-01 then ##show genre##
WHEN date_of_birth_cust > 1990-01-01 and date_of_birth_cust < 2000-01-01 then ##show genre##
ELSE ##show genre##
END
from purchases
INNER JOIN (
select id_cust
FROM sell_log
group by id_cust
) customer.id_cust = sell_log.id_cust
How is the correct form in your opinion?
Expected results: for example
based on the most frequent number of genres find that genre and pass it for that age group.
ages most frequent genre
from 1980 to 1990 comedy
from 1990 to 2000 fantasy
rest ages drama
Update:
doing the code in the answer gives this:
ages most_frequent_genre
from 1980 to 1989 Comedy
from 1990 to 1999 Thriller
from 1990 to 1999 Action
from 1990 to 1999 Comedy
rest Comedy
What am I doing wrong
You can use a CTE to get the results per age and genre and then use it to get the maximum number of purchases per age. Finally join again to the CTE:
with cte as (
select
CASE
WHEN year(c.date_of_birth_cust) between 1980 and 1989 then 'from 1980 to 1989'
WHEN year(c.date_of_birth_cust) between 1990 and 1999 then 'from 1990 to 1999'
ELSE 'rest'
END ages,
f.genres,
count(*) counter
from sell_log s
inner join films f on f.id_film = s.id_film
inner join customers c on c.id_cust = s.id_cust
group by ages, f.genres
)
select c.ages, c.genres most_frequent_genre
from cte c inner join (
select c.ages, max(counter) counter
from cte c
group by c.ages
) g on g.ages = c.ages and g.counter = c.counter
order by c.ages
See the demo.
In your sample data there are ties which will all be at the results.
Results:
| ages | most_frequent_genre |
| ----------------- | ------------------- |
| from 1980 to 1989 | fantasy |
| from 1990 to 1999 | comedy |
| rest | fantasy |

MYSQL Stuck Generating temp table (massive query)

I have 4 tables (1 to many):
Dont say anything about that "email" relation. It is how my developer boss built it years ago.
EMPLOYEES (+-50 results)
------------------------------------------------
id name
1 EmpName 1
2 EmpName 2
CUSTOMERS (+50k results)
------------------------------------------------
id name email employee_assigned
1 John john#doe.com 12
2 Donald donald#duck.com 6
INTERESTS_CATEGORIES (+650k results)
------------------------------------------------
id customer_email category_id
1 john#doe.com 97
2 john#doe.com 13
3 donald#duck.com 56
4 donald#duck.com 126
5 donald#duck.com 45
INTERESTS_PRODUCTS (+650k results)
------------------------------------------------
id customer_email product_id
1 john#doe.com 78
2 john#doe.com 23
3 donald#duck.com 19
4 donald#duck.com 56
5 donald#duck.com 45
So I need to filter the customers by their assigned employee and their interests.
And here is the query:
SELECT
*
FROM
(
SELECT
customers.id AS 'id',
customers.name AS 'first_name',
customers.email,
employees.id AS 'employee_id'
FROM
customers,
employees
WHERE
employees.id = 2
AND
customers.employee_assigned = employees.id
) AS myCustomers
LEFT JOIN interests_categories
ON interests_categories.customer_email = myCustomers.email
LEFT JOIN interests_products
ON interests_categories.customer_email = myCustomers.email
WHERE
(
interests_categories.category_id = 20
OR
interests_categories.category_id = 21
)
GROUP BY myCustomers.email
So, the problem:
If the employee has a low number of assigned customers (like 3) query
is successfull.
If the employee has a medium-high number of assigned customers (over 100) query stucks.
I execute SHOW PROCESSLIST and it is stucked "Generating temp table".
Anyone has idea? :(
Thank you.
Check the indexes on your tables and try this:
SELECT
c.id AS 'id',
c.name AS 'first_name',
c.email,
e.id AS 'employee_id'
ic.*,
ip.*
FROM customers c
JOIN employees e
ON c.employee_assigned = e.id
LEFT JOIN interests_categories ic
ON ic.customer_email = c.email
LEFT JOIN interests_products ip
ON ic.customer_email = c.email
WHERE
(
ic.category_id IN (20,21)
AND e.id = 2
)
GROUP BY myCustomers.email
Incidentally, a less dumb design might look like as follows. If it was me, I'd start with this, and provide properly representative CREATE and INSERT statements accordingly. Also, I'm curious about where category_id comes from - because that's potentially an area for further optimization.
EMPLOYEES
------------------------------------------------
employee_id name
6 EmpName 1
12 EmpName 2
CUSTOMERS
------------------------------------------------
customer_id name email employee_assigned
1 John john#doe.com 12
2 Donald donald#duck.com 6
INTERESTS_CATEGORIES
------------------------------------------------
customer_id category_id
1 97
1 13
2 56
2 126
2 45
INTERESTS_PRODUCTS
------------------------------------------------
customer_id product_id
1 78
1 23
2 19
2 56
2 45

MYSQL - Joining Multiple tables with COUNT()

I was asked to create a database for both students' tardiness and school policy's violations.
Now, I have three separate tables:
tbl_ClassList:
Student_ID Student_Name
1000 Lee, Jonder
1001 Chow, Stephen
1002 Kim, Martin
1003 Johns, Kevin
1004 Hearfield, Olivia
1005 Jarrs, Marlon
tbl_Tardy:
Record_No Student_ID
1 1001
2 1001
3 1000
4 1003
5 1002
6 1003
7 1001
8 1001
9 1002
10 1004
tbl_Violation:
Record_No Student_ID
1 1000
2 1000
3 1004
4 1005
5 1001
6 1002
7 1003
What I was asked to do is to generate a list that combines that contains information about the students including his/her ID, name, number of tardiness and the number of violations. Something like this:
Student_ID Student_Name No. of Tardy No. of Violation
1000 Lee, Jonder 1 2
1001 Chow, Stephen 4 1
1002 Kim, Martin 2 1
1003 Johns, Kevin 2 1
1004 Hearfield, Olivia 1 1
1005 Jarrs, Marlon 0 1
Is there any type of Joins I can use to achieve the output? Please help me.
You can find separate aggregates for tardy and violations inside subqueries and left join them with classlist table. Use coalesce to get zero in case there is no row for tardy/violation.
select c.*,
coalesce(t.count_tardy, 0) as no_of_tardy,
coalesce(v.count_violations, 0) as no_of_violations,
from tbl_classlist c
left join (
select student_id,
count(*) as count_tardy
from tbl_tardy
group by student_id
) t on c.student_id = t.student_id
left join (
select student_id,
count(*) as count_violations
from tbl_violation
group by student_id
) v on c.student_id = v.student_id;

Add total of 3 rows for specific id

I have three tables:
Students
-------------------------------------------------------------
studentId first last gender weight
-------------------------------------------------------------
1 John Doe m 185
2 John Doe2 m 130
3 John Doe3 m 250
Lifts
-------------------
liftId name
-------------------
1 Bench Press
2 Power Clean
3 Parallel Squat
4 Deadlift
5 Shoulder Press
StudentLifts
------------------------------------------------
studentLiftId studentId liftId weight
------------------------------------------------
1 1 1 185
2 2 3 130
3 3 1 190
4 1 2 120
5 2 1 155
6 3 2 145
7 1 1 135
8 1 1 205
9 2 3 200
10 1 3 150
11 2 2 110
12 3 3 250
I would like to have four top lists:
Bench Press
Parallel Squat
Power Clean
Total of the above 3
I can successfully grab a top list for each specific lift using the following query:
SELECT s.studentId, s.first, s.last, s.gender, s.weight, l.name, sl.weight
FROM Students s
LEFT JOIN (
SELECT *
FROM StudentLifts
ORDER BY weight DESC
) sl ON sl.studentId = s.studentId
LEFT JOIN Lifts l ON l.liftId = sl.liftId
WHERE l.name = 'Bench Press'
AND s.gender = 'm'
AND s.weight > 170
GROUP BY s.studentId
ORDER BY sl.weight DESC
However, I am stuck on how to add the highest total of each lift for each student. How can I first find the highest total for each student in each lift, and then add them up to get a total of all three lifts?
Edit
The result set that I am looking for would be something like:
-------------------------------------------------
studentId first last weight
-------------------------------------------------
3 John Doe3 585
1 John Doe 475
2 John Doe2 465
I also forgot to mention that I would actually like two lists, one for students above 170 and one for students below 170.
SELECT -- join student a total weight to the student table
A.studentId,
A.first,
A.last,
C.totalWeight
FROM
Student A,
(
SELECT -- for each studet add the max weights
sum(B.maxWeight) as totalWeight,
B.studentID
FROM (
SELECT -- for each (student,lift) select the max weight
max(weight) as maxWeight,
studentId,
liftID
FROM
StudentLifts
GROUP BY
studentId,
liftID
) B
GROUP BY
studentId
) C
WHERE
A.studentID = C.studentId
-- AND A.weight >= 170
-- AND A.weight < 170
-- pick one here to generate on of the two lists.

Calculate sum from three tables

I have 3 mysql tables:
Client_courses
Client_courses_id Client_id Course_id
1 1 2
2 1 3
3 2 1
Client
Client_id Name
1 Tom
2 John
Courses
Course_id Name Price
1 Microsofr 100
2 Programming 250
3 Leadership 300
I need to calculate how much every client spent money on courses. For example: Tom spent 550 (250+300), John spent 100. And I am confused how to do it.
SELECT SUM(c.Price), cl.Name
FROM Client cl
INNER JOIN Client_courses clc ON cl.Client_id=clc.Client_id
INNER JOIN Courses cs ON clc.Course_id=cs.Course_id
GROUP BY cl.Name