How to get names based on it roles - sql

Is there any way to display two person name in same row based on their role.
I having 4 tables which is person, student, teacher and TourAttend. Person is the parent of the two table student and teacher.
Person
-------
personID
Name
Role
Student
--------
StudentID
personID reference person
Teacher
-------
teacherID
personID reference person
TourAttend
-----------
TourID reference to Tour table (Which i didnt reveal)
TourAttendID primary key
PersonID reference person
example values of
PersonID | Name | Role
-----------------------
1 | student1| student
2 | student2| student
3 | teacher1| teacher
4 | teacher2| teacher
5 | student3| student
example values for TourAttend
TourID | tourAttend | PersonID
1 | 1 | 1
1 | 2 | 2
1 | 3 | 3
2 | 4 | 4
2 | 5 | 5
i would to get a query result that is each student is accompanied by who (teacher) Based on the Tour
Student Name | Teacher Name
student 1 | teacher 1
student 2 | teacher 1
student 3 | teacher 2
so far i only tried is
select person.name as 'student name' , person.name as 'Teacher name' from person
and i am stuck because the person.name is used for both. how do i resolve this problem?

This should work for you
select
t1.name as student_name, t2.name as teacher_name
from
(
select
p.name, t.TourID
from
Person p
inner join
TourAttend t
on
p.PersonID = t.PersonID
where
p.role = 'student'
) t1
inner join
(
select
p1.name, t3.TourID
from
Person p1
inner join
TourAttend t3
on
p1.PersonID = t3.PersonID
where
p1.role = 'teacher'
) t2
on
t1.TourID = t2.TourID;

Related

Consolidate values from multiple tables without duplicate it

I have tried to do the below example using joins and union but was not successful. I appreciate any assistance.
I have a Student table and 3 other tables of courses Planed to be done, current Enrolled and, Concluded course. For each of those courses tables, I have the FK_Student and the Course name. I do like to "join" all results in a single line for each course name with each course table as a column name. (The same course can be in multiple tables)
See example below:
Table: Student
Id_Student | Student
1 Bob
2 ...
Table: Planed
Id_Planed | Course | Fk_Student
1 History 1
2 English 1
3 Biology 1
4 Geometry 1
5 PE 1
6 Algebra 1
....
Table: Enrolled
Id_Enrolled | Enrollment | Fk_Student
1 History 1
2 Biology 1
3 PE 1
...
Table: Concluded
Id_Concluded | Conclusion | Fk_Student
1 History 1
2 English 1
3 Physics 1
...
Expected Result:
Student | Planed | Enrolled | Concluded
Bob History History History
Bob English NULL English
Bob Biology Biology NULL
Bob Geometry NULL NULL
Bob PE PE NULL
Bob Algebra NULL NULL
Bob NULL NULL Physics
FULL OUTER JOIN is used here because every subject name are not existed in all table. First subquery retrieve student wise Course, Enrollment and Conclusion record. Then INNER JOIN with student table as per expected output. If all student info needed then LEFT JOIN will be better. In Planed/Enrolled/Concluded table same Course/Enrollment/Conclusion can not assigned multiple time for particular student. As student_id and course needed for calculation so use two tables student_id and course inside COALESCE() so that always return NOT NULL value.
-- SQL SERVER (v2014)
SELECT s.Student, r.Course, r.Enrollment, r.Conclusion
FROM Student s
INNER JOIN (SELECT COALESCE(t.student_id, c.Fk_Student) student_id
, t.Course, t.Enrollment, c.Conclusion
FROM (SELECT COALESCE(p.Fk_Student, e.Fk_Student) student_id
, COALESCE(p.Course, e.Enrollment) Course_t
, p.Course
, e.Enrollment
FROM Planed p
FULL OUTER JOIN Enrolled e
ON p.Fk_Student = e.Fk_Student
AND p.Course = e.Enrollment) t
FULL OUTER JOIN Concluded c
ON c.Fk_Student = t.student_id
AND c.Conclusion = t.Course_t) r
ON s.Id_Student = r.student_id;
Please check from url https://dbfiddle.uk/?rdbms=sqlserver_2014&fiddle=09d03c0b64d31c8ae5a3b91145b7b7e5
It looks like your data model is somewhat flawed in its design, I would have expected a table of Courses with each course linked by a Course_Id to each student for each type.
I'm not clear on your desired output but it seems like you want a complete list of courses for each student and which of each are applicable in each case.
You can use a CTE to build a table of truth for all courses which is then cross-joined to Students so each student is presented with the full list of courses and then outer-joined to the 3 tables to indicate which courses are applicable to the student on each case.
with courses as (
select course from planed union
select enrollment from enrolled union
select conclusion from concluded
)
select s.Student, p.Course Planed, e.Enrollment Enrolled, cc.Conclusion Concluded
from courses c
cross join student s
left join planed p on p.course=c.course and p.fk_student=s.id_student
left join enrolled e on e.enrollment=c.course and e.fk_student=s.id_student
left join concluded cc on cc.Conclusion=c.course and cc.fk_student=s.id_student
Try this (no nested query or CTEs):
SELECT
s.Student,
p.Course,
e.Enrollment,
c.Conclusion
FROM Planed AS p
FULL JOIN Enrolled AS e
ON e.Fk_Student = p.Fk_Student AND
e.Enrollment = p.Course
FULL JOIN Concluded AS c
ON (c.Fk_Student = p.Fk_Student AND
c.Conclusion = p.Course) OR
(c.Fk_Student = e.Fk_Student AND
c.Conclusion = e.Enrollment)
RIGHT JOIN Student AS s
ON s.Id_Student IN (
p.Fk_Student,
e.Fk_Student,
c.Fk_Student
);
Result:
+---------+----------+------------+------------+
| Student | Course | Enrollment | Conclusion |
+---------+----------+------------+------------+
| Bob | History | History | History |
| Bob | English | | English |
| Bob | Biology | Biology | |
| Bob | Geometry | | |
| Bob | PE | PE | |
| Bob | Algebra | | |
| Bob | | | Physics |
| Sam | | | |
+---------+----------+------------+------------+
db<>fiddle

SQL Count people who have all 4 grandparents

So I have these following tables:
1) ADDRESS PERSON_ID | START_DATE | END_DATE | STREET | HOUSE | ROOM | ZIPCODE | CITY
2) IDENTIFIER NR | PERSON_ID | ISSUE_DATE | VALID_UNTIL
3) MARRIAGE WIFE_ID | HUSBAND_ID | START_DATE | END_DATE
4) PERSON ID | FIRST_NAME | LAST_NAME | BIRTHDATE | MOTHER_ID | FATHER_ID
And my task is to calculate the number of people who have all 4 grandparents recorded in the database.
So if I got it right, I have to find all the people in the table where both MOTHER_ID and FATHER_ID are not null, whose MOTHER_ID and FATHER_ID columns are also not null.
What I have for now is:
SELECT COUNT(*)
FROM PERSON
WHERE MOTHER_ID IS NOT NULL AND FATHER_ID IS NOT NULL
which returns the number of people who have both parents recorded in the database and I'm stuck at this point.
I'd join the table on itself twice, once for the father and once for the mother, and then check their parents:
SELECT COUNT(*)
FROM person p
JOIN person f ON p.father_id = f.id AND f.father_id IS NOT NULL AND f.mother_id IS NOT NULL
JOIN person m ON p.mother_id = m.id AND m.father_id IS NOT NULL AND m.mother_id IS NOT NULL
You should join two instance of Person table together and then select rows with four mother|father_1|2 is not null.
the sql is something like this.
SELECT p1.father_id AS father_id_1, p2.father_id AS father_id_2,p1.mother_id AS mother_id_1,p2.mother_id AS mother_id_2,
FROM Person p1
JOIN Person p2 ON p1.FATHER_ID = p2.ID and p1.MOTHER_ID = p2.ID

SQL Count Number Of Classmates For a Student Across All Courses

I have the following tables:
Student: Student_ID (PK), Student_Name
Course: Course_ID (PK), Course_Name
Enrollment: Student_ID (FK), Course_ID (FK)
I need 2 queries:
A query that computes for each student id in the Student table the total number of different (Unique) classmates that this student has across all courses. If the student is not enrolled in any courses 0 should be returned.
For example if Student_ID 123 is enrolled in 3 courses and he has 10 different classmates in each of those courses, I should get the following result:
Student_ID Num_of_classmates
-----------------------------------
123 30
A SQL query that returns all students with all of their classmates. classmate of a student is another student who is enrolled in at least one same class.
It is unclear which of these you want:
Total students in all the classes (which would include a given student).
Total distinct students, since some classmates might be in multiple classes.
Total distinct students not including the given student ("I am not my own classmate").
In any case, the idea is basically two joins and aggregation:
select s.student_id,
count(ec.student_id) as total_classmates,
count(distinct s.student_id) as distinct_classmates,
count(distinct case when ec.student_id <> s.student_id then ec.student_id end) as distinct_other_classmates
from student s left join
enrollment e
on e.student_id = s.student_id left join
enrollment ec
on ec.class_id = e.class_id;
group by s.student_id;
Here I only give a solution to part 2) as #Gordon Linoff has done part 1) and you have also fixed the -1 problem.
A point: use inner join instead of left join here to avoid NULL in classmate names. Hopefully this also adds a little bit of help :)
Test dataset
if object_id('tempdb..#Student') is not null drop table #Student;
create table #Student (
Student_ID int PRIMARY key,
Student_Name varchar(50)
)
insert into #Student(Student_ID, Student_Name)
values (1,'Alice'), (2,'Bob'),(3,'Claire'),(4,'Danny'),(5,'Eve'),(6,'Frank');
if object_id('tempdb..#Course') is not null drop table #Course;
create table #Course (
Course_ID int PRIMARY key,
Course_Name varchar(50)
)
insert into #Course(Course_ID, Course_Name)
values (1,'Algorithm'), (2,'Bash'),(3,'Compiler'),(4,'Design Pattern'),(5,'Exception Handling');
if object_id('tempdb..#Enrollment') is not null drop table #Enrollment;
create table #Enrollment (
Student_ID int,
Course_ID int
)
insert into #Enrollment(Student_ID, Course_ID)
values (1,1),(1,3),
(2,2),(2,3),
(3,3),(3,4),
(4,1),(4,4),
(5,1),
(6,5); -- This Frank guy has no classmate
-- select * from #Student;
-- select * from #Course;
-- select * from #Enrollment;
Solution to 2)
select distinct
A.Student_Name as Student_Name,
D.Student_Name as Classmate_Name
from #Student as A
-- Student (A) -> Enrolled Courses (B)
inner join #Enrollment as B
on A.Student_ID = B.Student_ID
-- Enrollment Courses (B) -> Enrolled Students in that Course (C)
inner join #Enrollment as C
on B.Course_ID = C.Course_ID
and B.Student_ID <> C.Student_ID -- exclude self
-- Classmate's names
inner join #Student as D
on C.Student_ID = D.Student_ID
order by Student_Name, Classmate_Name;
Output
N.B. Frank has no classmates and do not show a NULL value.
| Student_Name | Classmate_Name |
|--------------|----------------|
| Alice | Bob |
| Alice | Claire |
| Alice | Danny |
| Alice | Eve |
| Bob | Alice |
| Bob | Claire |
| Claire | Alice |
| Claire | Bob |
| Claire | Danny |
| Danny | Alice |
| Danny | Claire |
| Danny | Eve |
| Eve | Alice |
| Eve | Danny |

SQL Query to find total number of students enrolled in a subject

Class Table has class_id and class_name columns with the ff values:
class_id | class_Name
1 | Algebra
2 | History
3 | PE
Student table has student_id and student_name column:
student_id | student_name
1 | Kylo Ren
2 | Rey
The third table which is a junction table for both table has the following columns class_id and student_id
class_id | student_id
1 | 1
1 | 2
2 | 1
3 | 2
How can I find the number of students enrolled per subjects?
Output should be like this
Class Name | Number of Students
Algebra | 2
History | 1
PE | 1
Then after that edit the query so that it would only show the class name and number of students greater than 2
Thanks!
Aggregate the junction table by student, and then assert that a matching student has all courses:
WITH cte AS (
SELECT student_id
FROM junction
GROUP BY student_id
HAVING COUNT(class_id) = (SELECT COUNT(*) FROM class)
)
SELECF COUNT(*) AS num_all_classes FROM cte;
Maybe I got this wrong...
SELECT
c.class_name "Class Name",
count(distinct j.student_id) "Number of Students"
FROM
class c
LEFT JOIN junction j ON j.class_id = c.class_id
GROUP BY
1

Check the count of column value is equal to one based on condition in sql server

I have two tables
Student
StudentId | StudentName
---------- | --------------
1 | John
2 | Susan
3 | Andy
4 | Joe
Department
StudentId | DepartmentId
---------- | ------------
1 | 123
1 | 234
2 | 123
2 | 456
3 | 123
4 | 456
Each student can be in multiple departments but I have to find those students which are only in one department like student 3 and 4
Any help?
Use GROUP BY and HAVING
SELECT s.StudentId,s.StudentName
FROM Department d
JOIN Student s ON s.StudentId=d.StudentId
GROUP BY s.StudentId,s.StudentName
HAVING COUNT(d.DepartmentId)=1
And if you also want to show students which not in Department table
SELECT s.StudentId,s.StudentName
FROM Department d
RIGHT JOIN Student s ON s.StudentId=d.StudentId
GROUP BY s.StudentId,s.StudentName
HAVING COUNT(d.DepartmentId)<=1
Variant with LEFT JOIN
SELECT s.StudentId,s.StudentName
FROM Student s
LEFT JOIN Department d ON s.StudentId=d.StudentId
GROUP BY s.StudentId,s.StudentName
HAVING COUNT(d.DepartmentId)<=1
Try this,
SELECT st.StudentId, St.StudentName
FROM student st
JOIN Department dep ON dep.StudentId = st.StudentId
GROUP BY st.StudentId
HAVING COUNT(dep.DepartmentId) = 1;
You can use IN with department table group by StudentId alongwith having count(Student_Id) = 1 in sub-query as below.
SELECT *
FROM student s
WHERE s.StudentId IN (
SELECT d.StudentId
FROM Department d
GROUP BY d.StudentId
HAVING count(d.DepartmentId) = 1
);
Result:
+-----------+-------------+
| studentid | studentname |
+-----------+-------------+
| 3 | Andy |
| 4 | Joe |
+-----------+-------------+
DEMO
Simply join them & aggregate them by Group by clause with having.. count() function
SELECT s.StudentId, s.StudentName
FROM student s
JOIN Department d ON d.StudentId = s.StudentId
GROUP BY s.StudentId, s.StudentName
HAVING COUNT(d.DepartmentId) = 1;
Result :
studentid studentname
3 Andy
4 Joe
Another approach is to eliminate the students having multiple departments at the beginning by using a cte table, then use the table with IN keyword like:
with cte as (
select StudentId
from Department
group by StudentId
having count(StudentId)=1
)
select *
from Students
where StudentId in (select StudentId from cte)