I have the following tables:
Student Table
| id | name | gender|
|----|----------|-------|
| 1 | April | F |
| 2 | Jane | F |
| 3 | Joe | M |
| 4 | Mike | M |
Project Table
| project_id | student_id | project_name|
|------------|------------|-------------|
| 101 | 1 | Alpha |
| 101 | 2 | Alpha |
| 101 | 3 | Alpha |
| 102 | 2 | M |
| 102 | 4 | M |
| 103 | 1 | Beta |
| 103 | 3 | Beta |
Assume there are much more students and project ids.
Multiple students can work in the same project.
My question is, having the tables above, how can I check how many students who worked together on 2 or more projects? So in the example above, students with id 1 and 3 worked together in project Alpha and Beta.
My code so far is
SELECT * FROM student s
JOIN project s ON student.id = project.project_id
I know I want to join both tables by the column they share (which is the student id) but I have no idea what to do after. I am new to SQL barely a week in learning and would appreciate the most help.
Use a self join and aggregation:
select p1.student_id, p2.student_id, count(*) as num_projects
from projects p1 join
projects p2
on p1.project_id = p2.project_id and
p1.student_id < p2.student_id
group by p1.student_id, p2.student_id
having count(*) > 1
order by count(*) desc;
Consider:
select count(*)
from (
select 1
from projects p1
inner join projects p2
on p2.project_id = p1.project_id and p2.student_id < p1.student_id
group by p1.student_id, p2.student_id
having count(*) > 1
) t
The inner query self-joins the project table and generates unique tuples of students that worked on the same project; the having clause filters on tuples that worked together on more than one project.
The outer query just counts the number of tuples.
Related
I'm in front of a "minor" problem taht looks easy but I didn't suceed to resolve it.
I have three tables in my Database :
Table gp
____________
id | name |
____________
1 | Le Mans|
2 | Toulon |
3 | Rennes |
Table player
____________
id | name |
____________
1 | Thibaut|
2 | Fred |
3 | Samir |
Table Records
_____________________________
id | gp_id | player_id | time
_____________________________
1 | 1 | 1 | 17860
2 | 2 | 1 | 11311
3 | 3 | 1 | 33133
4 | 3 | 2 | 11113
5 | 2 | 2 | 44444
6 | 1 | 2 | 13131
7 | 1 | 3 | 11111
8 | 3 | 3 | 21112
I want to get a sum of time for players that have a record on every gp ( so in my case, just players Thibaut and Fred have a record on the 3 gp ( Samir has just a record on two gp ) ).
I have no idea how I can get that, of course this SQL query is retrieving a sum but from this query I want to escape the guys that don't have a record on every GPs, but I'm blocked at that point ...
SELECT p.name, sum(time)
from records r
join gp g on r.gp_id = g.id
join player p on r.player_id = p.id
group by r.player_id
Thanks in advance guys !
You could use having count to exclude the records that don't have a record on every GPs.
Try:
select p.name,
sum(`time`) as tot_sum
from records r
inner join player p on r.player_id=p.id
inner join gp g on g.id=r.gp_id
group by p.name
having count(distinct gp_id) = (select count(distinct id) from gp)
https://dbfiddle.uk/t8QwSFDY
having count(distinct gp_id) = (select count(distinct id) from gp) will match only the records in the record table that have a record on every gp.
I have a few tables which I am trying to join and fetch the results for a list
Interviews Table
+--------------+-----------+
| interview_id | Candidate |
+--------------+-----------+
| 1 | Ram |
| 2 | Rahim |
| 3 | Joseph |
+--------------+-----------+
Participant Ratings Table
+--------------+-----------+-------+
| interview_id | Rater Type|Rating |
+--------------+-----------+-------+
| 1 | Candidate | 4 |
| 2 | Candidate | 4 |
| 1 | Recruiter | 5 |
+--------------+-----------+-------+
System Ratings Table
+--------------+------------+-------+
| interview_id | Rating Type|Rating |
+--------------+------------+-------+
| 1 | Quality | 4 |
| 1 | Depth | 4 |
| 1 | Accuracy | 5 |
| 2 | Quality | 4 |
| 2 | Depth | 3 |
| 2 | Accuracy | 5 |
| 3 | Quality | 4 |
| 3 | Depth | 5 |
| 3 | Accuracy | 5 |
+--------------+------------+-------+
I need to fetch the result of average ratings for each interview given in the following manner.
+--------------+--------------+-----------------+-----------------+
| interview_id | System Rating|Recruiter Rating |Candidate Rating |
+--------------+--------------+-----------------+-----------------+
| 1 | 4.3 | 5 | 4 |
| 2 | 4.0 | 0 | 4 |
| 3 | 4.6 | 0 | 0 |
+--------------+--------------+-----------------+-----------------+
Each interview can will have one 1 candidate rating and 1 recruiter rating but that is optional. If given a record is created in participant rating with rating and type.
Need to get the average of system ratings of all the types and get one value as system rating and if rating provided by participants then display else display as 0 if any or both the participants not provided any rating.
Please ignore the values, if there is a mistake.
The SQL which I tried to get the result.
SELECT i.candidate, i.id AS interview_id,
AVG(sr.rating) AS system_rating,
AVG(CASE WHEN pr.rater_type = 'Candidate' THEN pr.rating END) AS candidate_rating,
AVG(CASE WHEN pr.rater_type = 'Recruiter' THEN pr.rating END) AS recruiter_rating
FROM system_ratings sr, participant_ratings pr, interviews i
WHERE sr.interview_id = i.id AND i.id = 2497 AND pr.interview_id = i.interview_id
The problem is whenever participant ratings are not present then results are missing as there is join.
Use LEFT JOIN to make sure if relation tables do not have any data, still we can have records from the main table.
Reference: Understanding MySQL LEFT JOIN
Issue(s):
Wrong field name: pr.interview_id = i.interview_id, it should be pr.interview_id = i.id as we don't have any interview_id field in interviews table, it would be id field - based on your query.
pr.interview_id = i.id in where clause: If participant_rating table does not have any records for a given interview, this will cause the removal of that interview from the result set. Use LEFT JOIN for participant_rating table.
sr.interview_id = i.id in where clause: If system_rating table does not have any records for a given interview, this will cause the removal of that interview from the result set. Use LEFT JOIN for system_rating table too.
Usage of AVG works but won't work for other aggregates functions like SUM, COUNT.. because if we have one to many relationships then join will make there will be multiple records for the same row.
Solution:
SELECT
i.id AS interview_id,
i.candidate,
AVG(sr.rating) AS system_rating,
AVG(CASE WHEN pr.rater_type = 'Candidate' THEN pr.rating END) AS candidate_rating,
AVG(CASE WHEN pr.rater_type = 'Recruiter' THEN pr.rating END) AS recruiter_rating
FROM interviews i
LEFT JOIN system_rating sr ON sr.interview_id = i.id
LEFT JOIN participant_rating pr ON pr.interview_id = i.id
-- WHERE i.id IN (1, 2, 3) -- use whenever required
GROUP BY i.id
I wanted to learn about web development so I made website with where users can vote on movies, and have issues with making a query for what I need. My tables are as follows:
--rtable--
+-----------+------------+------------+
| movieid | rating | userid |
+-----------+------------+------------+
| 1 | 9 | 27 |
| 2 | 8 | 27 |
| 1 | 10 | 31 |
| 1 | 7 | 42 |
| 2 | 8 | 31 |
+-----------+------------+------------+
--mtable--
+-----------+------------+------------+------------+
| movieid | moviename | movielink | director |
+-----------+------------+------------+------------+
| 1 | Foo | foo.com | bob |
| 2 | Bar | bar.com | steve |
+-----------+------------+------------+------------+
I wanted to make a query to for movie name, movie link, avg(rating), and the users rating (if exists), descending by avg(rating)
--desiredtable (if userid == 42)--
+-----------+------------+------------+------------+
| moviename | movielink | avgrating | yourrating |
+-----------+------------+------------+------------+
| Foo | foo.com | 8.66 | 7 |
| Bar | bar.com | 8 | NULL |
+-----------+------------+------------+------------+
I've managed to get moviename + movielink + avgrating working with OUTER LEFT JOIN but I'm scratching my head as to how to add yourrating. I've tried doubling up on OUTER JOIN and using sub-queries but can't seem to get it to work.
This is what I have so far that works
SELECT mtable.moviename, mtable.movielink, ROUND(AVG(rtable.rating), 2) AS avgrating,
FROM mtable LEFT OUTER JOIN rtable ON rtable.movieid = mtable.movieid GROUP BY mtable.charid ORDER BY AVG(rtable.rating) DESC
You need to join the rtable twice on the mtable, once to get all ratings for the average, once to get the user's rating. You also need to supply the userid for which r2 is filtered within the on clause. That filter criterion in the on clause will apply to r2 only, not the entire dataset.
SELECT mtable.moviename, mtable.movielink, ROUND(AVG(r1.rating), 2) AS avgrating, max(r2.rating) as yourrating
FROM mtable m LEFT OUTER JOIN rtable r1 ON r1.movieid = m.movieid
LEFT JOIN rtable r2 on r2.movieid=m.movieid and r2.userid=...
GROUP BY m.movieid, m.moviename, m.movielink
ORDER BY AVG(r1.rating) DESC
Good day fellow programmers. I have 3 tables, with following sample records.
tbl_members has:
mem_id | mem_fname | mem_lname
1 | Ryan | Layos
2 | Dhave | Sebastian
3 | Staven | Siegal
4 | Ma Ethel | Yocop
5 | Kelvin | Salvador
6 | Herbert | Ares
tbl_member_status has:
status_id | mem_id | leader_id | process_id
1 | 2 | 1 | 2
2 | 3 | 5 | 3
3 | 4 | 6 | 4
4 | 5 | 1 | 4
5 | 1 | 6 | 4
(tbl_member_status.mem_id is foreign keyed to tbl_members.mem_id, and leader_id is also foreign keyed to tbl_members.mem_id because in my case a member can be a leader. 1 member 1 leader)
tbl_process has:
process_id | process_type
1 | CONSOLIDATION
2 | PRE-ENCOUNTER
3 | ENCOUNTER
4 | POST-ENCOUNTER
(a member has a process to take which i used enum with values: CONSOLIDATION, PRE-ENCOUNTER, ENCOUNTER, POST-ENCOUNTER, etc.)
My question now is the proper sql query in getting the desired output query like this.
tbl_query_result
mem_id | member_fname | member_lname | leader_fname | leader_lname | process_type
2 | dhave | sebastian | Ryan | Layos | PRE-ENCOUNTER
5 | Kelvin | Salvador | Ryan | Layos | POST-ENCOUNTER
do remember that two columns of tbl_member_status is referring to one column of tbl_members that is mem_id.
UPDATE:
what i have done so far:
SELECT member.mem_fname, member.mem_lname, leader.mem_fname, leader.mem_lname, tbl_process.process_type
FROM
tbl_member_status as mem_stats
INNER JOIN
tbl_members as member
INNER JOIN
tbl_members as leader
INNER JOIN
tbl_members ON mem_stats.member_id = member.mem_id
INNER JOIN
tbl_process ON tbl_process.process_id = mem_stats.process_id
WHERE
leader.mem_fname = 'Ryan'
This query gets all record even if the leader.mem_fname is not equal to 'Ryan'
Because when you query. The number of rows in the result matters. Like: if your result is for fname = ryan but then the match for mem.id in table memberstatus is two and then in process table is again two. Inshort you will have 2 rows in final output.
Can you try this :
Select M.member_fname, M.mem_lname P.process_type from tbl_members M, tbl_member_status MS, tbl_process P where M.mem_id = MS.mem_id and MS.process_id = P.process_id and where M.member_fname = 'ryan'
Okay i misunderstood your question at first. I have a solution for you which will improve your database schema. If one member has only one leader and a single leader has many memebers. Then why not create a different table called leader and connect to members table directly? So it will be a one to one relation. Which will make querying much simpler. So now you have 4 tables.
Select M.member_fname, M.mem_lname, L.fname, L.lname, P.process_type
from tbl_members M, tbl_member_status MS, tbl_process P, tbl_leader L
where M.leader_id = L.id and M.mem_id = MS.mem_id and MS.process_id = P.process_id and where M.member_fname = 'ryan'
Here is a simplified version of the pertinent part of my DB:
Person:
id
PersonSkills:
person_id
skillname
ability
Position:
id
PositionSkills:
position_id
skillname
ability
A person can have any number of PersonSkills, let's say in this instance, our user has 6.
A Position can require any number of PositionSkills, let's say in this instance the position requires 3.
I need a query that will determine whether ALL of the PositionSkills associated with this Position are present in the PersonSkills associated with this person. (I also need to ensure that the PersonSkill's ability is greater than the PositionSkill's ability, but I think that will be simple once I figure out the part that is giving me trouble here.)
Thanks in advance,
Jason
EDIT
Here is more detail on what I'm looking for:
PersonSkills
+-------------+---------+---------+
| person_id | skill | ability |
+-------------+---------+---------+
| 1 | A | 5 |
| 1 | B | 4 |
| 1 | C | 5 |
| 1 | D | 4 |
| 1 | E | 5 |
+-------------+---------+---------+
PositionSkills
+-------------+---------+---------+
| position_id | skill | ability |
+-------------+---------+---------+
| 5 | A | 3 |
| 5 | B | 3 |
| 5 | C | 3 |
| 6 | A | 3 |
| 6 | B | 3 |
| 6 | Z | 3 |
+-------------+---------+---------+
From the fact that I'm user 1, I want a query to tell me that I am qualified for Position 5, because I have skills A, B, and C, that it requires, but that I am not qualified for Position 6, because I lack skill Z
Thanks again,
Jason
Try this solution:
SELECT
a.id,
(COUNT(c.position_id) = (SELECT COUNT(*) FROM positionskills WHERE position_id = <position_id here>)) AS isQualified
FROM
person a
LEFT JOIN
personskills b ON a.id = b.person_id
LEFT JOIN
positionskills c ON
b.skillname = c.skillname AND
b.ability >= c.ability AND
c.position_id = <position_id here>
GROUP BY
a.id
WHERE
a.id = <person_id here>
If the person is qualified, isQualified will be 1 else it will be 0
EDIT: As per clarification in the question, to get all the positions for which the person is qualified for, use this solution:
SELECT
a.position_id
FROM
(
SELECT bb.position_id, COUNT(*) AS skillshave
FROM personskills aa
INNER JOIN positionskills bb ON aa.skillname = bb.skillname AND aa.ability >= bb.ability
WHERE aa.person_id = <person_id here>
GROUP BY bb.position_id
) a
INNER JOIN
(
SELECT position_id, COUNT(*) AS skillsrequired
FROM positionskills
GROUP BY position_id
) b ON a.position_id = b.position_id AND a.skillshave = b.skillsrequired