Select where and where not - sql

I have a table containing lessons that I called "cours" (french) and I have several cours inside and I have linked them to students with a table between them to see if they go to the lessons or not.
I would like to return data with the SELECT and the data that are NOT select.
So, If one student follow 3 courses of 5, I would like to return the 3 courses that he follow and the 2 courses that he doesn't follow.
Is there a way to do it ?

This will vary based on your RBDMS, but something similar to the following should work:
SELECT
s.Name,
l.Name,
CASE WHEN sl.StudentID IS NULL
THEN 'Not Follows'
ELSE 'Follows'
END AS Status
FROM
Student s
CROSS JOIN Lessons l
LEFT JOIN Student_Lessons sl
ON s.ID = sl.StudentID
AND l.ID = sl.LessonID

this is an outer join. if you identify your database engine (which may have specific outer join syntax) and your schema, then you can get some help with the actual SQL.

Related

I need an JPA/SQL expert: EXISTS query on an Inner Join returns wrong result

I have three tables and want to:
Select all students from the first table,
that have at least one connection to the school in district '999' in the second table
and at least one connection to the teacher with social_number '101'
and at least one to the teacher with number '103' in the third table.
The tables are connected through the second table.
I created an online sql compiler to show the problem:
http://tpcg.io/FIoO79xi
This query works fine and as expected, until I add the third EXISTS Command where I search for a connection to teacher '103'. Then it doesn't return student A anymore, altough he has a connection to teacher '103'
I found a workaround by adding joins in the Exists sub-query:
http://tpcg.io/0sza7t5g
but since my real database tables have many million entries, this would lead to joining the three tables in every row that the sub-query goes through and this can take very long if it only finds a fitting entry at the end of the table.
I think the problem is here at the sub-query: WHERE th1.school_id = th.school_id where I'm trying to find a connection from the third table teacher to the at the beginning joined together table. If I search for a connection to teacher 102 instead of 103, the query works and returns student A:
http://tpcg.io/2tHIEk3V
Because teacher 101 and 102 have the same school_id.
But how can I write that differently so that the query also finds student A when I search for a connection to teacher 101 and 103? Student A has a connection to both, so it should be possible somehow with exists...
Add: I can't use three seperate queries and then use the Intersect command on them, since I'm translating that SQL into a JPA query. JPAdoesn`t know intersect...
The 1st condition:
at least one connection to the school in district '999' in the second
table
needs a join of student to school.
The 2nd and 3rd conditions:
at least one connection to the teacher with social_number '101'
and at least one to the teacher with number '103'
need 2 separate joins of student to school and teacher:
SELECT s.name
FROM student s
INNER JOIN school sc on s.student_id = sc.student_id AND sc.district = 999
INNER JOIN school sc1 on s.student_id = sc1.student_id
INNER JOIN teacher t1 on t1.school_id = sc1.school_id AND t1.social_number = 101
INNER JOIN school sc2 on s.student_id = sc2.student_id
INNER JOIN teacher t2 on t2.school_id = sc2.school_id AND t2.social_number = 103
Note that a condition like social_number in (101, 103) will not work because it would return results even if only 1 of the conditions was satisfied.
This is why you need 2 joins to school and teacher.
Also all the joins must be inner because you want to satisfy all 3 conditions.
See the demo.
Results:
| name |
| ---- |
| A |
You need 2 teacher table joins
SELECT name
FROM student
left JOIN school sc1 on #student.student_id = sc1.student_id
left JOIN teacher th1 on sc1.school_id = th1.school_id and th1.social_number=101
left JOIN teacher th2 on sc1.school_id = th2.school_id and th1.social_number=103
where sc1.district=999
Why so complicated ?
`SELECT name
FROM student
LEFT JOIN school sc1 on student.student_id = sc1.student_id
LEFT JOIN teacher th1 on sc1.school_id = th1.school_id
WHERE sc1.district = '999'
AND th1.social_number in('101','103')`
doesn't do the trick ?
http://tpcg.io/eGeWjkOf
The whole joining is unnecessary when we ask for a connection between the teacher and the student in an additional WHERE clause.
The Exists subquery then looks like this, instead of a Join I use an additional where to make sure the teacher.school_id is the same as the school.school_id of the school the student goes to:
EXISTS (
SELECT *
FROM teacher th
WHERE th.social_number = '103'
AND th.school_id in (SELECT school_id FROM school WHERE student_id = student.student_id ))

sql join tables IS NOT

I need to get the data of all teachers who gives a course but does not have an assignement. I tried using inner or left join so if the name of the teacher is not in the table of the assignments it will show the data. If the name of the teacher is in the table of assignments it must not show the data. But I could not get it working.
What am I doing wrong?
select teachers.nameteacher, courses.namecourse, courses.codecourse
from teachers
inner join courses
on teachers.codecourse = courses.codecourse
left join assignments
on assignments.nameteacher = teachers.nameteacher
where teachers.nameteacher IS NULL
filter should be from assignments table
SELECT t.nameteacher,
c.namecourse,
c.codecourse
FROM teachers t
INNER JOIN courses c
ON t.codecourse = c.codecourse
LEFT JOIN assignments a
ON a.nameteacher = t.nameteacher
WHERE a.nameteacher IS NULL --should be assignments
Note : start using alias names to make the query more readable
The easy (and efficient) way, if you are using PostgreSQL or Microsoft SQL Server is to say:
select
a.nameteacher,
c.namecourse,
c.codecourse
from
(
select nameteacher from teachers
except
select nameteacher from assignments
) a
join teachers b
on a.nameteacher = b.nameteacher
join courses c
on b.codecourse = c.codecourse;
If you are using Oracle change "except" to "minus".
If you are using SQLite or MySQL you need to use one of the other suggestions because these don't support except or minus.

What Join to use against 2 Tables for All Data

Hi I am looking to find out what join I would use if I wanted to join 2 tables together. I currently have a list of all students so 25 students to 1 class and the other table only shows 7 of those names with their test results.
What I would like is to have 1:1 join for the ones with the test results and the other ones without I would like to show them underneath so all in all I have 20 records.
If somebody could please advise on how I could achieve this please.
Thanks in advance.
It sounds like you want an OUTER JOIN.
For this example, we'll assume that there is a table named student and that it contains a column named id which is UNIQUE (or PRIMARY) KEY.
We'll also assume that there is another table named test_result which contains a column named student_id, and that column is a foreign key referencing the id column in student.
For demonstration purposes, we'll just make up some names for the other columns that might appear in these tables, name and score.
SELECT s.id
, s.name
, r.score
FROM student s
LEFT
JOIN test_result r
ON r.student_id = s.id
ORDER
BY r.student_id IS NULL
, s.score DESC
, s.id
Note that if student_id is not unique in test_result, there is potential to return multiple rows that match a row in student.
To get (at most) one row returned from test_result per student, we could use an inline view.
SELECT s.id
, s.name
, r.score
FROM student s
LEFT
JOIN ( SELECT t.student_id
, MAX(t.score) AS score
FROM test_result t
GROUP BY t.student_id
) r
ON r.student_id = s.id
ORDER
BY r.student_id IS NULL
, s.score DESC
, s.id
The expressions in the ORDER BY clause are designed to return the students that have matching row(s) in test_result first, followed by students that don't.
This is just a demonstration, and very likely excludes some important criteria, such as which test a score should be returned for. But without a sample schema and some example data, we're just guessing.
You are looking for a left outer join or a full outer join.
The left outer join will show all students and their tests if they have them.
select *
from Students as s
left outer join Tests as t
on s.StudentId = t.StudentId
The full outer join will show all students with their tests if they have them, and tests even if they do not have students.
select *
from Students as s
full outer join Tests as t
on s.StudentId = t.StudentId

EXISTS Syntax in sql?

I have two tables:
students(id, name, school_id)
schools(id, name)
I'm trying to use EXISTS in order to learn if there are any students that go to specific school, say "Harvard" for example. I know that EXISTS is used after WHERE but I'm wondering if I can do this:
SELECT EXISTS
(SELECT *
FROM students st, schools sch
WHERE st.school_id=sch.id AND sch.name="Harvard");
Is this query correct? I am working on MySQL Workbench and I don't get an error. But I don't know if it does what it's supposed to do.
If it's not, then what should I change? I just want to know if it's correct and if I can use this syntax in the future.
Note that the desired result is either yes or no (1 or 0).
How do I get this result?
Sorry if my question was unclear, I can edit it again if you still don't understand.
You could just get the count, then you would know if any exist - and if so, how many...
SELECT COUNT(*)
FROM students st, schools sch
WHERE st.school_id=sch.id AND sch.name="Harvard"
The EXISTS keyword is normally used as a pre-condition... i.e. "if not exists, then add this..."
This is a simple Select with a Join.
SELECT *
FROM Students S
JOIN Schools SC
ON S.School_id = SC.id
Where SC.Name = 'Harvard'
This will give you all the rows for the students that go to Harvard, if any. If you want to do a count you can do SELECT COUNT(*) instead or limit which columns are returned by indicating the specific columns in the SELECT statement
This will give you student names that go to Harvard. If you wish to see how many students learn at Harvard replace st.name with count(*).
Note that it doesn't matter what you put in SELECT list inside EXISTS statement, so choosing a constant value provides better performance than selecting columns.
SELECT
st.name
FROM
students st
WHERE
EXISTS(
SELECT 1 FROM schools s WHERE s.name= 'Harvard' AND s.id = st.school_id)
OR
SELECT
st.name
FROM
students st
INNER JOIN schools s ON
st.school_id = s.id
WHERE
s.name = 'Harvard'
Additional note: Code below would only yield result either true or false.
SELECT EXISTS ( <query> )
This means that below query would return true if there are any students that learn at Harvard.
SELECT EXISTS (
SELECT 1
FROM students st
INNER JOIN schools s ON st.school_id = s.id
WHERE s.name = 'Harvard' )

Where SQL column value not found in other column

I've read similar questions and I think I am doing it correct, but I just wanted to make sure my SQL is correct. (Still new to SQL)
I have 2 different tables
Students
id, name, address
Staff
id, name, address
I need to find the total number of students (who are not also staff)
SO I have the following SQL
create view nstudents as
select students.id
from students
LEFT JOIN staff ON staff.id = students.id;
Then I run the count(*) on the view.
Can someone confirm my SQL is correct or is there better way to do it?
Your LEFT JOIN doesn't eliminate students who are also staff, but it could be useful in achieving your goal. LEFT JOIN provides you with all results from the left table and matching results from the right table, or NULL results if the right table doesn't have a match. If you do this:
select count(*)
from students
LEFT JOIN staff ON staff.id = students.id
WHERE staff.id IS NULL;
I expect you'll get what you're looking for.
You might find it more natural to do something like this:
create view nstudents as
select s.id
from students s
where not exists (select 1 from staff st where st.id = s.id) ;
This should have the same performance as the left join.