sql query related - sql

Hi I have an "Student" table with below records
INSERT into Student(studId,name)
Values(1,A)
values(2,B)
values(3,C)
values(4,D)
I have a "Department" table with below records
INSERT into dept(deptId,Deptname,Emp Id)
Values(D1,Phy,1)
values(D2,Maths,2)
values(D3,Geo,3)
How can i find the student who does not belong to any department i.e. in this case the result should be "D".
I know the left outer join would return all the records from the student table but i am only interested to get 1 record i.e.: of student "D".

SELECT *
FROM Student
WHERE studId NOT IN (SELECT EmpId FROM dept)
Find all Student.studId entries that do not already exist in the dept.EmptId column as an entry. (Assuming I read your table correctly)
Ideally though, you should probably break out the "Student" and "Department" and creating a joining table (maybe called) "Student_Department" that links the keys of each table.
+--------------+ +--------------------+ +--------------+
| Student | | Student_Department | | Dept |
|--------------| |--------------------| |--------------|
| studId | <-----| studId | .-> | deptId |
| name | | deptId | --' | name |
| ... | +--------------------+ | ... |
+--------------+ +--------------+
This allows you to only have to define a student and department once, but can assign one student to multiple departments, one department to multiple students, or any combination therein.

Pseudo-code:
select *
from students
where id not in(select a.id from students a
inner join department b where b.id in('D1','D2','D3'))

While I agree with some commenters that this sounds like a homework problem, I'll answer with a question...
I know the left outer join would return all the records from the student table but i am only interested to get 1 record i.e.: of student "D".
OK, let's say you run the following query:
SELECT * FROM Student
LEFT OUTER JOIN dept ON Student.studId = dept.EmpId
You'd get the results:
studId name deptId deptName EmpId
1 A D1 Phy 1
2 B D2 Maths 2
3 C D3 Geo 3
4 D NULL NULL NULL
Can you add a WHERE clause to this query that will filter out only the data you want? :)

SELECT s.name
FROM Student s
LEFT JOIN Dept d ON d.empId = s.studId
WHERE d.empId IS NULL

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

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)

Select and group results using the same column as a parameter

I have a query that returns the following result (example):
+----+-----------+------------+
| ID | FirstName | CourseName |
+----+-----------+------------+
| 1 | Alice | X |
| 2 | Bob | X |
| 2 | Bob | Y |
+----+-----------+------------+
the query takes 3 tables (users, user-courses and course), and using JOIN returns the id of the user and his first name, and all the names of all courses he is in.
i need to create a query which returns users who are in specific courses, for example:
select all the users in course X: will return the details both of Alice and Bob.
select all users in courses X AND Y: will return only Bob, since alice isn't in course Y.
the result of the query X AND Y will be:
+----+-----------+
| ID | FirstName |
+----+-----------+
| 2 | Bob |
+----+-----------+
Assuming that user table and course table have an id and a name columns, and user-courses has only foreign key ids, you can do the following:
For the first question:
select u.* from user u
inner join user-courses uc on uc.user_id=u.id
inner join course c on c.id=uc.course_id and c.name='X';
It filters the user on inner joins, and filter the course on tha last part (c.name = 'X'). You can filter in any other way.
For the second one:
select * from user
where id in (
select distinct a.* from (
select user_id from user-courses uc inner join course c
on c.id=uc.course_id
where c.name='X'
) a
inner join (
select user_id from user-courses uc inner join course c
on c.id=uc.course_id
where c.name='Y'
) b
on a.user_id=b.user_id
);
MS-Access don't have intersect, so I used inner join (between a and b) to achieve the same results. A is the table with users from course 'X' and b from 'Y'. The inner join intersect both, resulting in users that are in both courses. Then I used the ids to filter.
I don't have MS-access, so I tried in PostgreSQL, but I used SQL-ANSI, so I hope so.

Optimizing WHERE clause SQL query

I'm using SQL Server. I find myself doing complex queries in the WHERE clause with the following syntax:
SELECT ..
WHERE StudentID IS NULL OR StudentID NOT IN (SELECT StudentID from Students)
was wondering if there's a better approach/more cleaner way to replace it with because this is a small example of the bigger query I'm doing which includes multiple conditions like that.
As you can see I'm trying to filter for a specific column the rows which its column value is null or not valid id.
EDIT
Courses:
|CourseID | StudentID | StudentID2|
|-----------------------------------|
| 1 | 100 | NULL |
| 2 | NULL | 200 |
| 3 | 1 | 1 |
Students
|StudentID | Name |
|--------------------
| 1 | A |
| 2 | B |
| 3 | C |
Query:
SELECT CourseID
FROM Courses
WHERE
StudentID IS NULL OR StudentID NOT IN (SELECT * FROM Students)
OR StudentID2 IS NULL OR StudentID2 NOT IN (SELECT * FROM Students)
Result:
| CourseID |
|-----------|
| 1 |
| 2 |
As you can see, course 1 and 2 has invalid students.
Alain was close, except the studentID2 column is associated with the courses table. Additionally, this is joining each studentID column to an instance of the students table and the final WHERE is testing if EITHER of the student ID's fail, so even if Student1 is valid and still fails on Student2, it will capture the course as you are intending.
SELECT
C.CourseID
FROM
Courses C
LEFT JOIN Students S
ON C.StudentId = S.StudentId
LEFT JOIN Students S2
OR C.StudentId2 = S2.StudentId
WHERE
S.StudentId IS NULL
OR S2.StudentID IS NULL
this is not a sure shot but i have had experience that this is better performer than the question one:
SELECT CourseID from Courses WHERE
Courses.StudentID NOT exists (SELECT 1 FROM Students where Students.StudentID=nvl(Courses.StudentID,-1));
Also Create an index on StudentId in the Students Table.
And if your data model supports create a primary key foreign key relationship between the 2 tables. That way u definitely avoid invalid values in the courses table.
After your update:
SELECT CourseID from Courses WHERE
Courses.StudentID NOT exists (SELECT 1 FROM Students where Students.StudentID=nvl(Courses.StudentID1,-1) or Students.StudentID=nvl(Courses.StudentID2,-1));
The NOT EXISTS pattern is fine, however, you have several ways to do that.
You should check here and here
For example with LEFT JOIN (two left joins since two variables are checked)
SELECT *
from Courses
LEFT JOIN Students Student1
on Courses.StudentId = Student1.StudentId
LEFT JOIN Students Student2
on Courses.StudentId2 = Student2.StudentId
WHERE
-- No matching Student
student1.StudentId IS NULL
and student2.StudentId IS NULL

How to add a column via a query which counts the total rows with a specific criteria in a table with circular relationship in MS ACCESS 2007

I have a simple table "Employees" with this fields:
ID, ParentID, Name
ParentID is Nullable since an employee may have no Manager.
This table has a one-to-many relationship with itself:
ID --one--to--many--> ParentID
Now I want a query which returns this columns:
Name, Count of rows where their ParentID equals to the current row ID (the row is the manager of that rows)
Sample Table:
ID | ParentID | Name
======================
1 | 0 | John
----------------------
2 | 1 | Bob
----------------------
3 | 1 | Alice
----------------------
4 | 3 | Jack
This way I can find an employee is the manager of how many other employees.
The result should be something like this:
Name | Count of Employees
==========================
John | 2
--------------
Bob | 0
--------------
Alice | 1
--------------
Jack | 0
How can I achieve this in MS ACCESS 2007?
* I have tried built-in query builder without any success.
So you just want a count of how many employees a manager has?
You will have to join the table to itself. It can be done in the query builder but the SQL statement will be easier to put here.
SELECT [Employees].Name, [Employees].ParentID, [Employees].Name, Count([Employees1].ParentID) AS NoOfEmployees
FROM [Employees] LEFT JOIN [Employees] AS [Employees1] ON [Employees].ID = [Employees1].ParentID
GROUP BY [Employees].ID, [Employees].ParentID, [Employees].Name;
I added the two other columns from the tables and renamed the count column to NoOfEmployees.
Do you mean:
SELECT t1.ID, t2.ParentID
FROM Employees t1
LEFT JOIN Employees t2
ON t1.ID = t2.ParentID
To count you would say
SELECT t1.ID, Count(t2.ParentID)
FROM Employees t1
LEFT JOIN Employees t2
ON t1.ID = t2.ParentID
GROUP BY t1.ID