Sql Query to get data from associative table - sql

I have to fetch data from two tables
(PK) = Primary Key
(FK) = Foreign Key
TABLE1- [STUDENTS]
s_id(PK) name other
1 a z
2 b z
3 c z
TABLE2- [CLASSES]
c_id(PK) class_name
1 5th
2 6th
3 7th
TABLE3- [STUDENT-CLASS]
id(PK) student_id(FK) class_id(FK)
1 1 1
2 1 2
3 2 1
4 2 2
5 3 1
6 1 3
I want to display students with current classes(Last assigned class)
tables relations is as
when student gets admitted it is assigned class 1
after 1 year a new record is inserted in [STUDENT-CLASS] table assigning new class to each or some student
I want to display like this
s_id name other [STUDENT-CLASS].Class_id [CLASSES].class_nam
1 a z 3 7th
2 b z 2 6th
3 c z 1 5th

Try something like this
Select S.studentid, s.name, s.other, c.classid, c.classname
from
(Select studentid, Max(Classid) as 'currentclassid' from StudentClassTable group by studentid) A
inner join StudentTable S on A.studentid = S.Studentid
inner join ClassTable C on A.CurrentClassid = C.Classid

The following query will do the job.
SELECT student_id, name, other, b.last_class_id, c.class_name
FROM STUDENTS a
LEFT JOIN (SELECT student_id, max(class_id) As last_class_id
FROM student_class
GROUP BY student_id) b ON a.student_id = b.student_id
LEFT JOIN CLASSES c ON c.class_id = b.last_class_id

Related

Need SQL query to export students attendance report by classes

I have 3 tables:
students table
id
name
1
Jhon
2
Emma
3
Oliver
classes table
id
name
1
Math
2
Science
attendance table
id
student_id
class_id
1
1
1
2
1
2
3
2
1
I tried to write an SQL query to retrieve the students who attended both math and science classes:
SELECT s.id, s.name
FROM attendance a
INNER JOIN students s ON a.student_id = s.id
WHERE a.class_id IN (1,2);
the above code result is
id
name
1
Jhon
2
Emma
1
Jhon
But Emma only attended Math class.
I know this behavior is because of WHERE IN, it's the same as WHERE a.class_id = 1 OR a.class_id = 2.
But what should I do to get the expected results, Which is "Jhon" or only Jhon's ID because he is the only one who attended Math and Science Classes?
Either join to attendance twice, once for each class:
SELECT s.id, s.name
FROM students s
JOIN attendance a1 ON a1.student_id = s.id and a1.class_id = 1
JOIN attendance a2 ON a2.student_id = s.id and a2.class_id = 2
or join to both classes at once and use group by with having:
SELECT s.id, s.name
FROM students s
JOIN attendance a ON a.student_id = s.id and class_id in (1, 2)
GROUP BY s.id, s.name
HAVING COUNT(*) = 2
Using a CTE would help, and can help you extend to multiple classes. You are also missing a DISTINCT keyword, which is why Jhon appears twice. Maybe something like this:
WITH attendance_count(student_id, classes_attended)
AS (
SELECT
student_id,
COUNT(id) AS classes_attended
FROM attendance a
-- change this to change the classes
WHERE a.class_id IN (1,2)
GROUP BY student_id
)
SELECT DISTINCT s.id, s.name
FROM attendance_count
INNER JOIN students s ON attendance_count.student_id = s.id
-- if different number of classes, change this
WHERE classes_attended = 2

SQL query Postgres 12

I'm doing an inner join on a table like this:
SELECT *
FROM patient p
INNER JOIN
vaccine v
ON
p.vaccine_id = v.id
The condition f.vac_1 = mv.id might not been satisfied in the case where a person have not been vaccinated. In such case, I don't want to ignore the row, but instead of displaying the vaccine name (which is the purpose of the inner join) to display an emtpy string.
How can this be done ?
Example
Table vaccinne
id
name
1
Moderna
2
Comirnaty
3
Janssen
Table patient
id
name
vaccine_id
1
john
1
2
kermit
2
3
jessica
I'm looking for a query to produce:
id
name
vaccine_id
1
john
Moderna
2
kermit
Comirnaty
3
jessica
If I understand correctly, you want a left join starting with foo:
SELECT *
FROM foo f LEFT JOIN
vac v
ON f.vac_1 = mv.id

SQL - only select results that do not have a specific status value in a corresponding table

I have two tables, and I only want to get the Student IDs where they have perfect attendance for all months (they do not have a PerfectAttendance value of N for any month). These tables will have hundreds of millions of rows, so I was trying to come up with an approach that doesn't require a full separate subquery. If anyone has any recommendations, please let me know:
Table Student:
ID Name
------------
1 A
2 B
Table Attendance:
ID Month PerfectAttendance
---------------------------------
1 1 Y
1 2 Y
1 3 Y
1 4 Y
1 5 Y
1 6 Y
1 7 Y
1 8 Y
1 9 Y
1 10 Y
1 11 Y
1 12 Y
2 1 Y
2 2 Y
2 3 Y
2 4 Y
2 5 Y
2 6 Y
2 7 Y
2 8 Y
2 9 Y
2 10 Y
2 11 Y
2 12 N
SELECT *
FROM dbo.Student S
WHERE NOT EXISTS(SELECT 1 FROM dbo.Attendance
WHERE PerfectAttendance = 'N'
AND ID = S.ID);
My suggestion for this would be to query the table and get the number of months that each student has perfect attendance. Once you've done that, you can filter on the count being 12 (since there are twelve months).
Try this:
SELECT s.id, s.name, COUNT(*) AS numPerfectMonths
FROM student s JOIN attendence a ON s.id = a.id
WHERE a.perfectAttendance = 'Y'
GROUP BY s.id
HAVING COUNT(*) = 12;
Here is the SQL Fiddle for you.
EDIT
I made the assumption you will have 12 rows for each student. However, let's say you ran this in October and you want to see which students have a perfect attendance up to that point. You can use a subquery to pull for students without perfect attendance, and filter them out using NOT IN like so:
SELECT id
FROM student
WHERE id
NOT IN(SELECT s.id
FROM student s JOIN attendance a ON s.id = a.id
WHERE a.perfectAttendance = 'N'
GROUP BY s.id
HAVING COUNT(*) > 0);
Have an updated SQL Fiddle. To test this one, try deleting one of the rows for id number 1, and you'll still see that they are returned with perfect attendance.
Assuming you have 12 records per student in attendance table based on your data , you can do it with GROUP BY and HAVING clause.
SELECT S.ID, S.NAME
FROM Student S
JOIN Attendance A
on S.ID = A.ID
AND A.PerfectAttendance = 'Y'
GROUP BY S.ID, S.NAME
HAVING COUNT(*) = 12
I think Lamak's answer is probably the clearest and best-performing, but here is another variation on the GROUP BY method suggested by others, when you don't specifically look for a total of 12 months:
;WITH PerfectAttendance AS (
SELECT a.id
FROM Attendance a
GROUP BY a.id
HAVING MIN(a.PerfectAttendance) = 'Y'
)
SELECT s.id, s.Name
FROM PerfectAttendance p
JOIN Student s ON p.id = s.id;

SQL Server matching all rows from Table1 with all rows from Table2

someone please help me with this query,
i have 2 tables
Employee
EmployeeID LanguageID
1 1
1 2
1 3
2 1
2 3
3 1
3 2
4 1
4 2
4 3
Task
TaskID LanguageID LangaugeRequired
1 1 1
1 2 0
2 1 1
2 2 1
2 3 1
3 2 0
3 3 1
LangaugeID is connected to table langauge (this table is for explaination only)
LangaugeID LanguageName
1 English
2 French
3 Italian
is there a possilbe way to make a query which gets employees where they can speak all the languages required for each task?
for example:
Task ID 1 requires only LanguageID = 1, so the result should be EmployeeID 1,2,3,4
Task ID 2 requires all 3 languages, so the result should be EmployeeID 1,4
Task ID 3 requires only LanguageID = 3, so the result should be EmployeeID 1,2,4
here is another variant to do this:
select t1.taskid, t2.employeeid from
(
select a.taskid, count(distinct a.languageid) as lang_cnt
from
task as a
where a.LangaugeRequired=1
group by a.taskid
) as t1
left outer join
(
select a.taskid, b.employeeid, count(distinct b.languageid) as lang_cnt
from
task as a
inner join
employee as b
on (a.LangaugeRequired=1 and a.languageid=b.languageid)
group by a.taskid, b.employeeid
) as t2
on (t1.taskid=t2.taskid and t1.lang_cnt=t2.lang_cnt)
###
here you can insert where statement, like:
where t1.taskid=1 and t2.employeeid=1
if such query returns row - this employee can work with this task, if no rows - no
###
order by t1.taskid, t2.employeeid
as you see, this query creates two temporary tables and then joins them.
first table (t1) calculates how many languages are required for each task
second table (t2) finds all employees who has at least 1 language required for task, groups by task/employee to find how many languages can be taken by this employee
the main query performs LEFT JOIN, as there can be situations when no employees can perform task
here is the output:
task employee
1 1
1 2
1 3
1 4
2 1
2 4
3 1
3 2
3 4
update: simpler, but less correct variant, because it will not return tasks without possible employees
select a.taskid, b.employeeid, count(distinct b.languageid) as lang_cnt
from
task as a
inner join
employee as b
on (a.LangaugeRequired=1 and a.languageid=b.languageid)
group by a.taskid, b.employeeid
having count(distinct b.languageid) = (select count(distinct c.languageid) from task as c where c.LangaugeRequired=1 and c.taskid=a.taskid)
Another version using NOT EXISTS
Retrieve all task-employee combinations where a missing language does not exist
SELECT t1.EmployeeId, t2.TaskId
FROM (
SELECT DISTINCT EmployeeID
FROM Employee
) t1 , (
SELECT DISTINCT TaskID
FROM Task
) t2
WHERE NOT EXISTS (
SELECT 1 FROM Task t
LEFT JOIN Employee e
ON e.EmployeeID = t1.EmployeeID
AND e.LanguageID = t.LanguageID
WHERE t.TaskID = t2.TaskID
AND LanguageRequired = 1
AND e.EmployeeID IS NULL
)
http://www.sqlfiddle.com/#!6/e3c78/1
You could use a Join logic to get the result, something like:
SELECT a.EmployeeID FROM Employee a, Task b WHERE b.LanguageRequired == a.LanguageID;

How to count linked entries in another table with a specific value

Let's say I have two tables. A students table and an observations table. If the students table looks like:
Id Student Grade
1 Alex 3
2 Barney 3
3 Cara 4
4 Diana 4
And the observations table looks like:
Id Student_Id Observation_Type
1 1 A
2 1 B
3 3 A
4 2 A
5 4 B
6 3 A
7 2 B
8 4 B
9 1 A
Basically, the result I'd like from the query would be the following:
Student Grade Observation_A_Count
Alex 3 2
Barney 3 1
Cara 4 2
Diana 4 0
In other words, I'd like to gather data for each student from the students table and for each student count the number of A observations from the observations table and tack that onto the other information. How do I go about doing this?
This is a simple join and aggregate:
select
a.Student,
a.Grade,
count(b.Id) as Observation_A_Count
from
Student a left join
Observations b on a.Id = b.Student_Id
group by
a.Student,
a.Grade
order by
1
Or, you can use a correlated subquery:
select
a.Student,
a.Grade,
(select count(*) from observations x where x.Student_Id = a.Id) as Observation_A_Count
from
Student a
order by
a.Student
You can join the table with a specific condition, by doing this you can have a field for Observation_B_Count and Observation_C_Count, etc.
SELECT Student.Student, Student.Grade, COUNT(Observation_Type.*) AS Observation_A_Count
FROM Student
LEFT JOIN Observations ON Observations.Student_ID = Student.Student_ID AND Observations.Observation_Type = 'A'
GROUP BY Student.Student, Student.Grade