How to combine two tables in SQL Server - sql

I have these two tables:
Table 1
Date Name StudentID TotalDuration
------------------------------------------------------------
2019-09-30 aA 11111 100
2019-09-30 bB 22222 40
2019-09-30 cC 33333 60
2019-10-07 aA 11111 50
2019-10-07 bB 22222 10
2019-10-07 cC 33333 12
2019-10-07 dD 44444 90
It contains data of students who ATTENDED a lecture (hence, does not include those who did not attend.
Table 2
StudentID Surname FirstName Group
------------------------------------------------------------
11111 A a 1
22222 B b 1
33333 C c 1
44444 D d 2
55555 E e 2
66666 F f 2
77777 G g 3
Table2 contains data of ALL students in the class.
Name attribute in Table 1 is combination of surname + first name and TotalDuration is duration of student participation in minutes.
I want to combine these two tables so that it list all the students in the class and their TotalDuration.
I tried OUTER JOIN and UNION ALL, but can't figure out how I can list all the student, yet shows NULL value for those students who did not attend to the lecture on particular date.
How can I achieve this?

You could left join and group by. But since you just need one aggregate computation, a correlated subquery is probably a simpler and more efficient approach (if you have the right index in place - see below):
select
t2.*,
(
select sum(totalduration)
from table1 t1
where t1.studentid = t2.studentid
) totalduration
from table2 t2
For performance, consider an index on table1(studentid, totalduration).
Side note: from a database design perspective, column Name in table1 is just not needed; this is redondant information, that complicates maintenance task (what if someone changes the first name of a studend in the other table?). You should remove that column, and rely on the foreign key on studentid only.

You probably want to aggregate the result by StudentID and use a LEFT JOIN, as in:
select
a.StudentId,
a.FirstName,
a.Surname,
a.Group,
sum(b.TotalDuration) as total_duration
from table2 a
left join table1 b on b.StudentId = a.StudentId
group by a.StudentId
Note that grouping by a.FirstName and a.Surname (assuming StudentId is the primary key) is optional according to the SQL Standard. I'm not sure SQL server will require it, though.

Related

SQL Query: Join (or select) 2 columns from 1 table with 1 column from another table for a view without extra join columns

This is my very first Stackoverflow post, so I apologize if I am not formatting my question correctly. I'm pounding my head against the wall with what I'm sure is a simple problem. I have a table with a bunch of event information, about 10 columns as so:
Table: event_info
date location_id lead_user_id colead_user_id attendees start end <and a few more...>
------------------------------------------------------------------------------------------------
2020-10-10 1 3 1 26 2100 2200 .
2020-10-11 3 2 4 18 0600 0700
2020-10-12 2 5 6 6 0800 0900
And another table with user information:
Table: users
user_id user_name display_name email phone city
----------------------------------------------------------------------
1 Joe S goofball ...
2 John T schmoofball ...
3 Jack U aloofball ...
4 Jim V poofball ...
5 Joy W tootball ...
6 George A boring ...
I want to create a view that has only a subset of the information, not full table joins. The event table lead_user_id and colead_user_id columns both refer to the user_id column in the users table.
I want to create a view like this:
date Location Lead Name CoLead Name attendees
---------------------------------------------------------------------
2020-10-10 1 Jack U Joe S 26
2020-10-11 3 John T Jim V 18
2020-10-12 2 Joy W George A 6
I have tried the following and several iterations like it to no avail...
SELECT
E.date, E.location,
U1.display_name AS Lead Name,
U2.display_name AS CoLead Name.
E.attendees
FROM
users U1, event_info E
INNER JOIN
event_info E ON U1.user_id = E.lead_user_id
INNER JOIN
users U2 ON U2.user_id = E.colead_user_id
And I get the dreaded
You have an error in your SQL Syntax
message. I'm not surprised, as I've really only ever used joins on single columns or nested select statements... this two columns pointing to one is throwing me for a loop. Help!
correct query for this matter
SELECT
E.date, E.location,
U1.display_name AS Lead Name,
(select display_name from users where user_id=E.colead_user_id) AS CoLead Name,
E.attendees
FROM
event_info E
INNER JOIN
users U1 ON U1.user_id = E.lead_user_id

Query to find missing teacher-student combination

I have a single table with two columns, TEACHER_ID and STUDENT_ID, which holds the data of all teachers teaching the students. One teacher can teach multiple students and one student can be taught by many teachers.
TEACHER_ID STUDENT_ID
100 123
100 124
100 125
100 126
101 123
101 124
101 125
102 123
102 124
102 125
102 126
103 123
103 127
The need is to find which teacher is not teaching which student, i.e. it should show the output as the same 2 columns but the pair should be such that it doesn't exist in the table.
For example: student_id 127 is taught by teacher_id 103 alone, and similarly for all such missing pairs...
We can create a cross join to get all possible combinations and using the MINUS operator discard the actual data from the result leaving us with rest of the pairs.
But is there a better and efficient way to do this?
As for your question. You need to create all combinations first there isnt another way to find the missing pair.
SQL DEMO
With teachers as (
SELECT DISTINCT "TEACHER_ID"
FROM Table1
), students as (
SELECT DISTINCT "STUDENT_ID"
FROM Table1
)
SELECT teachers."TEACHER_ID" , students."STUDENT_ID"
FROM teachers
CROSS JOIN students
LEFT JOIN Table1 t
ON teachers."TEACHER_ID" = t."TEACHER_ID"
AND students."STUDENT_ID" = t."STUDENT_ID"
WHERE t."TEACHER_ID" IS NULL
ORDER BY 2, 1
You can do this by generating all combinations of teacher and student using cross join. Then filter out the ones that exist:
select t.teacher_id, s.student_id
from (select distinct teacher_id from t) t cross join
(select distinct student_id from t) s
minus
select t.teacher_id, s.student_id
from t;
For the filtering part, you can also use not in, not exists, or left join/where.

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.

Selecting only rows with the highest value of one field, grouped by another field

I have a table that has information structured like this:
ID Points Name School
1 123 James A
2 534 Henry B
3 56 Henry B
4 153 Chris B
5 95 Chris B
6 83 Chris B
7 421 James A
And I need to get out of a query the rows that have the same name, but only the highest points for each like this:
ID Points Name School
7 421 James A
2 534 Henry B
4 153 Chris B
Any ideas on how this could be accomplished with a query? I've spent way too much time trying to figure this out.
select name,school,max(points) from table group by name,school
That will give you the max points per name/school combination. Join it to itself if you want the ID:
select table.* from table inner join
(select name,school,max(points) as points from table group by name,school) a
on a.name = table.name and a.school = b.school and a.points = table.points
edit : sorry, this is a SQL solution...just saw the MSACCESS tag. Logic is right, but you'll need to convert to access syntax.
edit to correct the second query, missed a column inh my join
SELECT
(SELECT TOP 1 ID FROM Table
WHERE
Name = t.Name AND
School=t.School AND
Points=t.Points
) as Id, t.Name, t.Points, t.School
FROM
(SELECT Name, School, max(Points) as Points
FROM Table
GROUP BY Name, School) t

Should I Use a Self-Join

If I have a table...
ID Name Manager
0 Joe Sue
1 Jake Tom
0 Joe Tom
2 Larry Red
2 Larry Paul
1 Jake Paul
I want the output to be....
ID Name Manager1 Manager2
0 Joe Sue Tom
1 Jake Tom Paul
2 Larry Red Paul
Thanks...
If I have understood your request properly, yes, something like would produce the results you are looking for.
SELECT
t1.Name Name,
t1.Manager Manager1,
t2.Manager Manager2
FROM
Table t1
inner join Table t2 on t1.Manager = t2.Name
Of course a foreign key back to the index column would be preferential to strong comparisons for performance.
Yeah, if your table was called 'Managers':
SELECT Mgr1.ID,Mgr1.Name,Mgr1.Manager,Mgr2.Manager
FROM Managers AS Mgr1
LEFT JOIN Managers AS Mgr2
ON Mgr1.ID=Mgr2.ID
If your keeping the tables a join would be best.
If you hate joins, you could combine the max and min managers, and even then it would work if there is always 2 managers and they can't have the same name.
The below should work if I remember how to join up 2 queries correctly. but i would advise to see if it is possible to rework your table, have a separate table linking people to each other in a manager employee relation.
SELECT DISTINCT
F.ID, F.Name, S.Manager, F.Manager
FROM
(SELECT
ID, Name, MIN(manager) manager
FROM Managers
GROUP BY ID, Name) F,
(SELECT
ID, Name, MAX(manager) manager
FROM Managers
GROUP BY ID, Name) S
WHERE
F.ID = S.ID
AND S.Manager <> F.Manager
AND F.ID < S.ID