How to Join three tables with multiple values rows returned on the other two - sql

Say for example.
I have three separate tables:
course Table which has CourseId, StudentIds etc
student which of course contains student data and StudentName
score table
I only want one column from each table and fuse them into one.
CourseId StudentName Scores
---------- ------------- ----------
1 Gashio 10
1 Gashio 20
1 Lee 35
1 Lee 40
1 Edith 5
2 Lana 3
2 Reisha 50
For every Course there's multiple students, and for every Scores there's multiple scores they get from the course for a month.
I wanted something like this as a result:
CourseId StudentName Scores
--------- ------------- -------------
1 Gashio 10|20
1 Lee 35|40
1 Edith 5
2 Lana 3
2 Reisha 50
Since the scores return multiple values, I wanted it to become one column separated by a delimeter.
I'm not sure if this is where I should be using STRING_AGG?

You need STRING_AGG and GROUP BY
SELECT course.CourseId,
student.StudentName,
STRING_AGG(Scores, ,'|') AS Scores
FROM course INNER JOIN
student ON student.StudentId = course.StudentId INNER JOIN
score ON score.studentId = student.StudentId
GROUP BY cource.CourseId,
student.StudentName

use string_agg() with delimeter
select CourseId,StudentName,string_agg(Scores,'|') as scores
from tablename
group by CourseId,StudentName

Related

ORACLE DB query - maximum stipend in faculty

So, i have an task in uni to get max stipend in each faculty from a table with stipends.
Faculty table is:
ID_FACULTY FACULTY_NAME DEAN TELEPHON
---------- ------------------------------ -------------------- --------
10 Informacijas tehnologiju Vitols 63023095
11 Lauksaimniecibas Gaile 63022584
12 Tehniska Dukulis 53020762
13 Partikas tehnologijas Sabovics 63021075
Money table is:
ID_PAYOUT STUDENT_ID PAYOUT_DA STIPEND COMPENSATION
---------- ---------- --------- ---------- ------------
100 1 24-SEP-20 45.25 15
101 7 20-SEP-20 149.99 0
102 3 18-SEP-20 100 0
103 17 02-SEP-20 90.85 20
104 9 03-SEP-20 85 20
105 19 09-SEP-20 70.75 0
106 25 15-SEP-20 55 15
107 17 17-SEP-20 105.54 0
108 15 22-SEP-20 94 0
109 27 28-SEP-20 100 20
And the student table is:
ID_STUDENT SURNAME NAME COURSE_YEAR FACULTY_ID BIRTHDATE
---------- ------------------------- -------------------- ----------- ---------- ---------
1 Lapa Juris 4 13 27-SEP-96
3 Vilkauss Fredis 2 10 17-MAY-99
5 Karlsone Rasa 1 11 13-MAR-00
7 Grozitis Guntars 3 12 16-APR-97
9 Sonciks Jurgis 2 10 17-MAR-99
11 Berzajs Olafs 3 10 14-FEB-97
13 Vike Ilvija 2 13 14-MAY-99
15 Baure Inga 3 11 12-APR-97
17 Viskers Zigmunds 2 13 15-AUG-99
19 Talmanis Harijs 3 13 15-JUL-97
21 Livmanis Indulis 1 10 19-JAN-00
23 Shaveja Uva 2 13 18-FEB-98
25 Lacis Guntis 4 10 17-SEP-96
27 Liepa Guna 4 11 18-AUG-96
29 Klava Juris 2 10 19-MAY-98
I have tried many variations of queries, i think that I even tried all the possible combinations of joins, but i cannot achieve the neccessary result.
One of my queries looked like this:
SQL> SELECT ROW_NUMBER() OVER (ORDER BY surname) "Nr.",
f.faculty_name,
s.surname,
s.name,
MAX(m.stipend)
FROM faculty f, student s INNER JOIN money m ON s.id_student = m.student_id
WHERE s.faculty_id = f.id_faculty
GROUP BY f.faculty_name, s.surname, s.name
ORDER BY s.surname;
Which returned me the following result:
Nr. FACULTY_NAME SURNAME NAME MAX(M.STIPEND)
---------- ------------------------------ ------------------------- -------------------- --------------
1 Lauksaimniecibas Baure Inga 94
2 Tehniska Grozitis Guntars 149.99
3 Informacijas tehnologiju Lacis Guntis 55
4 Partikas tehnologijas Lapa Juris 45.25
5 Lauksaimniecibas Liepa Guna 100
6 Informacijas tehnologiju Sonciks Jurgis 85
7 Partikas tehnologijas Talmanis Harijs 70.75
8 Informacijas tehnologiju Vilkauss Fredis 100
9 Partikas tehnologijas Viskers Zigmunds 105.54
9 rows selected.
So the goal of this task is to retrieve the maximum amount of stipend granted to a student in a certain faculty.
Can someone please tell what am I doing wrong here?
Just max amount per faculty:
SELECT
f.faculty_name,
MAX(m.stipend)
FROM
faculty f
INNER JOIN student s ON s.faculty_id = f.id_faculty
INNER JOIN money m ON s.id_student = m.student_id
GROUP BY f.faculty_name
Max amount and all other details too:
SELECT * FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY f.faculty_name ORDER BY m.stipend desc) rn,
f.*,
s.*,
m.*
FROM
faculty f
INNER JOIN student s ON s.faculty_id = f.id_faculty
INNER JOIN money m ON s.id_student = m.student_id
) x
WHERE x.rn = 1
Points of note:
Do not use old style joins; if you ever write one table_name, other_table_name in a FROM block, you're using old style joins. Don't do it; they became bad news about 30 years ago
When you have a max-n-per-group, you specify how finely detailed the group is. If you GROUP BY s.first_name, s.last_name, f.faculty_name then your groups are "every unique combination of firstname/lastname/faculty, so the only way you'll get multiple items in your group is if there are two John Smiths in Mathematics. If the group is to be the whole of mathematics, then the faculty name (and anything else that is uniquely related 1:1 to it, like the faculty ID) is all that you can put in your group. Anything not in a group must be in an aggregation, like MAX
When you want other details too, you either group and max the data and then join this groupmaxed data back to the original data to use it as a filter, or you use an approach like here where you use a row_number or rank, with a partition (which is like an autojoined grouped summary). There is no group here; the row numbering acts like a group because it restarts from 1 every different faculty and proceeds incrementally as stipend decreses. This means that the highest stipend is always in row number 1.
Unlike using a groupmax that you join back to get the detail, the row_number route does not produce duplicate rows with tied-for-highest stipends

ORACLE SQL Multi Join

I want to join fields from different tables into one doing a query with joins. From now I've only joined two tables but I have difficulties merging others more. Can you help me? These are my tables:
Table Departments
------------------------------------
Department_ID Department_Name
------------------------------------
1 Sales
2 Marketing
3 Warehouse
Table Roles
---------------------------------
Role_ID
---------------------------------
1
2
3
4
Table Departments_Roles
--------------------------------------------------------------------
Dep_Role_ID Department_ID Role_ID Role_Name
--------------------------------------------------------------------
1 1 1 Admin
2 1 2 Client Attention
3 2 1 Admin
4 3 2 Client Attention
Table Employers
---------------------------------
Employer_Id Employer_Name
---------------------------------
1 John
2 Jess
3 Tom
4 George
5 David
What I want to see is:
Table Merged
-------------------------------------------------
Department_Name Employer_Name Role_Name
-------------------------------------------------
xxxxx yyyyy zzzz
This are just some example tables. Dont look for the sense of it.
I've tried using join, but I've never make something so complex.
Can you give me some advice?
Joining multiple tables is the same as joining one table, just repeated.
SELECT D.Department_Name, E.Employer_Name, DR.Role_Name
FROM Employers E --this is your base table
INNER JOIN magicalEmployerToDepartmentRoleLinkTable EDR --this connects our base table to the linking table
ON E.Employer_Id = EDR.Employer_Id
INNER JOIN Department_Roles DR --now we can pull any column in the Department_Roles table that is related back to our base table
ON ED.Department_Id = DR.Department_Id
INNER JOIN Department D --now we can pull any column in the Department table that is related back to the Department_Roles table
ON DR.Department_ID = D.Department_Id

Querying 100k records to 5 records

I have a requirement in such a way that it should join two tables with more than 100k records in one table and just 5 records in another table as shown below
Employee Dept Result
id Name deptid deptid Name Name deptid Name
1 Jane 1 1 Science Jane 1 Science
2 Jack 2 2 Maths Dane 1 Science
3 Dane 1 3 Biology Jack 2 Maths
4 Drack 3 4 Social Drack 3 Biology
5 Drim 5 Zoology Kery 4 Social
6 Drum 5 Drum 5 Zoology
7 Krack
8 Kery 4
.
.
100k
Which join need to be used to get the query in an better way to perform to get the result as shown.
I just want the query to join with other table from employee table only which has dept which i thought of below query but wanted to know is there any better way to do it.
Select e.name,d.deptid,d.Name from
(Select deptid,Name from Employee where deptid IS NOT NULL) A
and dept d where A.deptid=d.deptid;
Firstly not sure why you are performing your query the way you are. Should be more like
SELECT A.name, D.deptid,D.Name
FROM Employee A
INNER JOIN dept D
ON A.deptid = D.deptid
No need of the IS NOT NULL statement.
If this is a ONE TIME or OCCASIONAL thing and performance is key (not a permanent query in your DB) you can leave out the join altogether and do it using CASE:
SELECT
A.name, A.deptid,
CASE
WHEN A.deptid = 1 THEN "Science"
WHEN A.deptid = 2 THEN "Maths"
...[etc for the other 3 departments]...
END as Name
FROM Employee A
If this is to be permanent and performance is key, simply try applying an INDEX on the foreign key deptid in the Employee table and use my first query above.

Create view by joining three tables in SQL

I have three tables STUDENTS, SUBJECTS, RANK ,with data as -
1) STUDENTS [NAME(Primary)]
NAME
--------
Alex
Greg
2) SUBJECTS [ID(Primary)]:
ID
--------
100
101
102
3) RANK [SEQ(Primary), NAME, ID, RANK]
SEQ NAME ID RANK
------ ------- ------ ------
1 Alex 100 A
2 Greg 100 A
3 Greg 101 B
I want to create a view that should display data as
NAME ID RANK
------- ------ ------
Alex 100 A
Alex 101 Z
Alex 102 Z
Greg 100 A
Greg 101 B
Greg 102 Z
So, for every student and for every subject, the View should display the RANK if present in RANK table, else replace the NULL with 'Z'.
I'm a newbie to SQL. So any help in forming the query would be deeply appreciated!
cross join student and subject then left outer join the result with rank to get ranks for all (student, subject) combination. selecting column with NVL OR COALESCE will replace NULL with 'z'.
SELECT st.name,
su.id,
NVL(ra.rank,'Z') Rank, --COALESCE(ra.rank,'Z') Rank
FROM student st
CROSS JOIN subject su
LEFT OUTER JOIN rank ra
ON ra.name = st.name
AND ra.id = su.id
ORDER BY st.name,su.id
Note : ORDER BY can be removed from above query if you don't need.
fiddle
SELECT r.NAME, r.ID, NVL(r.RANK, 'Z')
FROM RANK r, studendts st, SUBJECTS su
WHERE st. NAME = r. NAME
AND su.ID = r.ID
ORDER BY 1,2,3

Joining multiple tables with a single query

Student
student_id FirstName LastName
---------------------------------------------------
1 Joe Bloggs
2 Alan Day
3 David Johnson
Student_Course
course_id student_id courseName
---------------------------------------------------
1 1 Computer Science
2 1 David Beckham Studies
3 1 Geography
1 3 Computer Science
3 3 Geography
Student_clubs
club_id student_id club_name club_count
---------------------------------------------------
1 1 Footbal 10
2 1 Rugby 10
3 1 Syncronized Swimming 10
4 3 Tennis 15
In the above example, student with id = 1 takes 3 course and is part of 3 clubs.
If i was to find out which courses a student is involved in or which club the student is part of i can do it but i will need to run two queries. Is it possible to run a single query against the
tables listed above so that the results come out like this:
Output
student_id FirstName Student_associated_courses Student_associated_clubs
---------------------------------------------------------------------------
1 Joe 1,2,3 Football, Rugby, Syncronized swimming
3 David 1,3 Tennis
Is it possible to get the above output with just one query? I am using JDBC to get the data so i am trying to see if i can avoid multiple trips to get the necessary data.
use GROUP_CONCAT with DISTINCT in MySQL
SELECT a.student_ID, a.firstname,
GROUP_CONCAT(DISTINCT b.course_ID),
GROUP_CONCAT(DISTINCT c.club_name)
FROM student a
INNER JOIN student_Course b
ON a.student_id = b.student_ID
INNER JOIN student_clubs c
ON a.student_ID = c.student_ID
GROUP BY a.student_ID, a.firstname
See SQLFiddle Demo
Try it like this:
SELECT *
FROM Student s JOIN
(SELECT sc."student_id", listagg(sc."course_id", ',')within group(ORDER BY sc."course_id")
FROM Student_Course sc
GROUP BY sc."student_id") s_course ON s."student_id"=s_course."student_id"
JOIN (SELECT sl."student_id", listagg(sl."club_name", ',')within GROUP(ORDER BY sl."club_name")
FROM Student_clubs sl
GROUP BY sl."student_id") s_club ON s."student_id"=s_club."student_id"
The "catch" is that LISTAGG doesn't work with DISTINCT keyword
Here is a fiddle