where not exists clause sql - sql

So I am trying to list students that are in at least one class but are not part of a group. My code is showing up 0 results but there should be two. Something is wrong with my where clause. I can get it to show students taking one or more classes, just cant get it to show the ones that are also not part of any group. There is something wrong with my where clause. I have 3 tables that are related. Student which holds student name and ID, Member of, which holds student ID and group ID, and studentgroup which holds group ID.
Code:
select student.lastname
from student inner join enrolled on enrolled.studentid = student.sid
where not exists(
select *
from studentgroup inner join memberof on memberof.groupid = studentgroup.gid
)

You don't have any condition that states how the student would be associated with the student group. So, the EXISTS always returns true, but you want to see the rows where the EXISTS returns false.
You can add the WHERE in the sub query to associate the student with the group.
SELECT student.lastname
FROM student INNER JOIN enrolled ON enrolled.studentid = student.sid
WHERE NOT exists(
SELECT *
FROM studentgroup INNER JOIN memberof ON memberof.groupid = studentgroup.gid
WHERE student.sid = memberof.studentid
)

what if you changed the where clause from where not exists to where not in? something like
where student.id not in (select distinct student_id from member_of)

Related

SQL Group by not returning accurate answer

it is current result
select cat.sms_schoolcategoryid as categoryId,
cat.sms_name as category ,
count(sch.sms_name) as schoolname,
count(stu.accountnumber)NoofStudent
from Filteredsms_schoolcategory cat
inner join Filteredsms_school sch
on cat.sms_schoolcategoryid=sch.sms_schoolcategoryid
inner join FilteredAccount stu
on sch.sms_schoolid=stu.sms_schoolid
group by cat.sms_schoolcategoryid,
cat.sms_name
;
I have three tables one is Category and 2nd is Schools and 3rd is students. i just want to count the schools on behalf of category when I join tables category and school it returns me accurate result and when i join students table with schools table it returns me wrong result. Please Guide me how it is possible.
There is guesswork involved unless you provide more information on you data model. However, it appears that your issue is rooted in the following:
When you join the student table, for each combination of school category and school (*) you generate additional records. I.e., your sql no longer counts schools by school category but students.
For a concrete solution and a good advice see #Nick.McDermaid's comment.
Try this one
select count(stu.accountnumber) as NoofStudent
, catsch.categoryId
, catsch.category
, catsch.schoolname
from FilteredAccount stu
inner join (
select cat.sms_schoolcategoryid as categoryId
, cat.sms_name as category
, count(sch.sms_name) as schoolname
, sch.sms_schoolid
from Filteredsms_schoolcategory cat
inner join Filteredsms_school sch
on cat.sms_schoolcategoryid = sch.sms_schoolcategoryid
group by cat.sms_schoolcategoryid, cat.sms_name, sch.sms_schoolid) catsch
on catsch.sms_schoolid = stu.sms_schoolid
group catsch.categoryId
, catsch.category
, catsch.schoolname
count() returns the number of non-NULL values. So, your two count() will return the same values. You can quickly fix the query using count(distinct):
select cat.sms_schoolcategoryid as categoryId,
cat.sms_name as category ,
count(distinct sch.sms_name) as schoolname,
count(distinct stu.accountnumber) as NoofStudent
from Filteredsms_schoolcategory cat inner join
Filteredsms_school sch
on cat.sms_schoolcategoryid = sch.sms_schoolcategoryid inner join
FilteredAccount stu
on sch.sms_schoolid = stu.sms_schoolid
group by cat.sms_schoolcategoryid, cat.sms_name ;
Actually, you probably don't need the second count distinct. Just count(stu.accountnumber) should count the students.

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

SQL select multiple rows and order them by id

I got a Student with a few parameters, like Id, Name, etc.
Now I got another one table called ACCOUNTS, they have an id and a name...
the relation between these two is OneToMany (one student can have more accounts)
and i need a SQL query to show all accounts for the student...here is what i have, but it's not working...
"Select distinct s from Student s left join fetch s.accounts where s.id=:studId"
I should say that the Student has a field called accounts
And at the end of the query, i placed already ORDER BY and then the following code didn't work:
s.accounts.id
account.id
student.account.id
So...long story short...the query above, shows the accounts of the specific student...now i just need to order them by the id of the accounts
Any suggestions?
As OP confirmed adding it as answer.
select distinct s.id, s.name, a.id
from students s left outer join accounts a on s.id = a.id
order by s.id, a.id;
one student can have more accounts
So it's two tables:
student (id, name, ...)
accounts (id, student_id, ...)
I should say that the Student has a field called accounts
That makes no sense. By this you would store one account per student and several students could share one account. So I stick to the first statement that one student can have several accounts and the tables look more or less like I've shown above.
You want to see accounts for one student. So you select from accounts where the student ID matches:
select * from accounts where student_id = :studId order by account.id;
If you want to show student data along, you'd join the tables instead:
select *
from student s
left join accounts a on a.student_id = s.id
where s.id = :studId
order by a.id;

SQL Count and Group by - Do count and group, used together, allow for this?

i'm working on an exercise query, using standard SQL.
What I need, in this example, is get the Name and the ID of every student who has enrolled in any course more than 2 times. In this case, the tables have the following information (I'll list only the columns that matter for this exercise):
Student has the ID and the name of the student.
Course has the course ID.
Class has the group ID and the course ID.
Registration has the reg. ID, the student ID, the class ID and the grade obtained.
So, it all sums up to find out which students have more than 2 entries in Registration that are matched to the same course, via Class. So far I've gotten this:
SELECT Student.id, Student.name FROM
Student S JOIN Registration R on S.id = R.studentID
WHERE (SELECT COUNT(*) FROM
Course C JOIN Class L on L.courseId = C.id
JOIN Registration R on R.classId = L.id
group by C.id ) > 2
The question is, do Count and Group by work this way?? Do they allow me to get the amount of matches on each group, or do they just give me the results on the set, as I fear they do?
If so, any ideas on how may I approach this problem??
Thanks for the help!!
Start reading up on the HAVING clause. Also, you aliases Student as S, so you need to use the prefix "S" for the SELECT clause, not "Student"
SELECT S.id StudentID, S.name, C.id CourseID
FROM Student S
JOIN Registration R on S.id = R.studentID
JOIN Class L on R.ClassId = L.id
JOIN Course C on L.courseId = C.id
GROUP BY S.id, S.name, C.id
HAVING COUNT(R.studentID) > 2;
All 4 tables are joined to produce a resultset representing all students registrated for any class of a course. Then we GROUP BY the student-course combination, and find out the ones where there are more than 2 registrations using the HAVING clause. Of course, if you meant "2 or more" instead of "more than 2", you'd use > 1 or >= 2 instead of > 2.

SQL Query Help - Return row in table which relates to another table row with max(column)

I have two tables:
Table1 = Schools
Columns: id(PK), state(nvchar(100)), schoolname
Table2 = Grades
Columns: id(PK), id_schools(FK), Year, Reading, Writing...
I would like to develop a query to find the schoolname which has the highest grade for Reading.
So far I have the following and need help to fill in the blanks:
SELECT Schools.schoolname, Grades.Reading
FROM Schools, Grades
WHERE Schools.id = (* need id_schools for max(Grades.Reading)*)
SELECT
Schools.schoolname,
Grades.Reading
FROM
Schools INNER JOIN Grades on Schools.id = Grades.id_schools
WHERE
Grades.Reading = (SELECT MAX(Reading) from Grades)
Here's how I solve this sort of problem without using a subquery:
SELECT s.*
FROM Schools AS s
JOIN Grades AS g1 ON g1.id_schools = s.id
LEFT OUTER JOIN Grades AS g2 ON g2.id_schools <> s.id
AND g1.Reading < g2.Reading
WHERE g2.id_schools IS NULL
Note that you can get more than one row back, if more than one school ties for highest Reading score. In that case, you need to decide how to resolve the tie and build that into the LEFT OUTER JOIN condition.
Re your comment: The left outer join looks for a row with a higher grade for the same school, and if none is found, all of g2.* columns will be null. In that case, we know that no grade is higher than the grade in the row g1 points to, which means g1 is the highest grade for that school. It can also be written this way, which is logically the same but might be easier to understand:
SELECT s.*
FROM Schools AS s
JOIN Grades AS g1 ON g1.id_schools = s.id
WHERE NOT EXISTS (
SELECT * FROM Grades g2
WHERE g2.id_schools <> s.id AND g2.Reading > g1.Reading)
You say it's not working. Can you be more specific? What is the answer you expect, and what's actually happening, and how do they differ?
edit: Changed = to <> as per suggestion in comment by #potatopeelings. Thanks!
This should do it
select * from Schools as s
where s.id=(
select top(1) id_schools from grades as g
order by g.reading desc)