Selecting exact value in SQL - sql

I am stuck on a query and decided to ask for help here.
I have 2 tables Students and Values. In students I have the Name and in Values I have the grade.
Let's suppose we have 3 students.
**X** 7,8,10
**Y** 6,9,7
**Z** 7
How can i select the students with the grade EXACTLY "7"?
I tried:
SELECT WHERE grade = 7 But it takes in the consideration the students who have "7" but also other grades too.
I think this problem is tricky, can someone give a hint?

One way is to use a comparison between unconditional and conditional counts:
select s.student_name
from students s join grades g on s.student_id = g.student_id
group by student_id
having count(*) = count(case when g.value = 7 then 1 end)
;
(guessing at some column names along the way)
How it works: After joining the two tables, the rows are grouped by student_id. Then COUNT(*) counts all the grades, and the conditional count counts the grades equal to 7. The query returns the student names when the two counts are equal (meaning all the grades are 7).
Another solution (less efficient):
select s.student_name
from students s inner join grades g on s.student_id = g.student_id
where g.grade = 7
minus
select s.student_name
from students s inner join grades g on s.student_id = g.student_id
where g.grade != 7 or g.grade is null
;

One simple way is to look at the task like this: Find students that have no grade other than seven.
select *
from students
where not exist
(
select *
from grades
where grades.student_id = students.student_id
and grades.grade <> 7
);
or
select *
from students
where student_id not in
(
select student_id
from grades
where grade <> 7
);

I think that an INNER JOIN followed by an exclusive LEFT JOIN would do the trick. Something like this:
WITH
seven AS (
SELECT a.id, a.name, b.grade
FROM student_name a
INNER JOIN student_grade b
ON a.id = b.id
WHERE b.grade = '7'
)
, not_seven AS (
SELECT a.id, a.name, a.grade
FROM seven a
LEFT JOIN student_grade
ON a.id = b.id
WHERE b.id IS NULL
AND b.grade <> '7'
)
SELECT * FROM not_seven;

what about using count and sum? Would that work?
select s.student, Count(v.grade), sum(v.grade)
From student as s
join values as v on v.student = s.student
group by student
having sum(v.grade) = 7 and count(v.grade) = 1

Related

Display courses with at least 10 students

I have the following tables:
Students (id, name, surname, study_year, department_id)
Courses(id, name)
Course_Signup(id, student_id, course_id, year)
I want to display the courses to which at least 10 students have signed up for, only using subqueries (no group-by, join or set operations). This could be easily implemented using data aggregation and join:
SELECT c.name, COUNT(csn.course_id)
FROM Course_Signup csn
JOIN Courses c
ON csn.course_id = c.id
GROUP BY c.name
HAVING COUNT(csn.course_id) >= 10
But how would I do this only using subqueries? Is there any other way, other than COUNT, to get the number of courses? Thank you, in advance!
You can use a correlated sub-query to retrieve the name:
SELECT (SELECT c.name FROM Courses c WHERE csn.course_id = c.id) AS name,
COUNT(*)
FROM Course_Signup csn
GROUP BY
course_id
HAVING COUNT(*) >= 10
Note: you should also GROUP BY the primary key the uniquely identifies the course as there may be two courses with the same name.
If you also don't want to use GROUP BY then:
SELECT name
FROM Courses c
WHERE 10 <= ( SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id )
or, to also get the number of sign-ups:
SELECT *
FROM (
SELECT name,
( SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id ) AS num_signups
FROM Courses c
)
WHERE num_signups >= 10;
You could do:
SELECT c.name
FROM Courses c
WHERE (
SELECT COUNT(*)
FROM Course_Signup csn
WHERE csn.course_id = c.id
) >= 10
which only uses a subquery and has no group-by, join or set operations.
fiddle
If you wanted the actual count in the result set then you would need to repeat the subquery in the select list.
You might also need to do COUNT(DISTINCT cs.student_id) if there might be duplicates; particularly if the same student can sign up in multiple years - but then you might want to restrict to a single year anyway.

Write a query to display the student names and the maximum mark scored by them in any subject

I have tried using group by and getting an error message of single row function cannot return multiple values.
It has three tables to be selected student, subject and mark.
If you only required student_id and MAX number, you can use only tables Student and Marks as below-
SELECT A.stident_id,MAX(B.Value) max_marks
FROM Student A
INNER JOIN Mark B ON A.Student_id = B.Student_id
GROUP BY A.stident_id
But if you need subject name as well, you can try this below logic-
SELECT AA.stident_id,AA.stident_name,
D.Subject_name,AA.max_marks
FROM
(
SELECT A.stident_id,A.stident_name,MAX(B.Value) max_marks
FROM Student A
INNER JOIN Mark B ON A.Student_id = B.Student_id
GROUP BY A.stident_id
)AA
INNER JOIN Marks C ON AA.stident_id = C.stident_id
AND AA.max_marks = C.Value
INNER JOIN Subject D ON C.subject_id = D.subject_id

ORACLE SQL check if the row number of the table is n, then perform a join

TABLE student: ID, ID2, NAME, AGE
TABLE class: ID, CLASS_NAME, some other columns
TABLE school: ID2, some other columns.
I am trying to perform below in Oracle SQL:
If the count of the records in TABLE "student" with age>5, is 1,
join the student table with "CLASS" table by "ID", else, join the student table with "school" table by "ID2".
I found I cannot put count in where clause. Can someone help?
I would use window functions:
select s.*, . . .
from (select s.*, sum(case when age > 5 then 1 else 0 end) over () as cnt5
from students s
) s left join
class c
on c.id = s.id and cnt5 = 1 left join
school sch
on sch.id2 = s.id2 and cnt5 <> 1
where c.id is not null or sch.id is not null
you can use left join with case
select s.*, case when age > 5 then COALESCE (c.ID,scl.ID2) as id
from student s
left join class c on s.ID=c.ID
left join school scl on s.ID2=scl.ID2
As Gordon said window functions are an option, but this is also a rare occasion where you can deliberately perform a cross join:
select s.*
,...
from students s
left join
(
select count(*) as RECORD_COUNT
from students
where age > 5
) sub
on 1 = 1
left join class c
on s.id = c.id
and sub.record_count = 1
left join school h
on s.id2 = h.id2
and c.id is null

Select students not having any corresponding course

I have a simple problem but I want to use Not having. I have two tables: student_table, and student_course
STUDENT (
ID,
Name
)
STUDENT_COURSE (
ID,
student_ID,
course_ID
)
I want to select those student name that does not have any corresponding course_ID in the STUDENT_COURSE table, so I write
SELECT *
FROM STUDENT
LEFT JOIN STUDENT_COURSE ON STUDENT_COURSE.STUDENT_ID = STUDENT.ID
but how can I write Not having in the next?
To find STUDENT with no STUDENT_COURSE, you could use different methods. See this article for the comparisons:
using NOT EXISTS:
SELECT s.*
FROM STUDENT s
WHERE NOT EXISTS(
SELECT 1 FROM STUDENT_COURSE c WHERE c.STUDENT_ID = s.ID
)
using LEFT JOIN
SELECT
s.*
FROM STUDENT s
LEFT JOIN STUDENT_COURSE c
ON c.STUDENT_ID = s.ID
WHERE c.ID IS NULL
using NOT IN
SELECT *
FROM STUDENT
WHERE ID NOT IN(
SELECT STUDENT_ID FROM STUDENT_COURSE
)
Just Add [STUDENT_COURSE].[STUDENT_ID] IS NULL filter which will find the students not invloved in any course
Left Outer join will produce NULL values for the right table columns which don't have a match.
SELECT *
FROM [STUDENT]
LEFT JOIN [STUDENT_COURSE]
ON [STUDENT_COURSE].[STUDENT_ID] = STUDENT.ID
WHERE [STUDENT_COURSE].[STUDENT_ID] IS NULL

sql get students names who passed in all subjects

I have 3 tables
students(id,name)
subjects(id,name)
student_subjects(student_id,subject_id,mark)
I want to select student names who passed all subjects (who have mark>=50 in all subjects)
I tried this:
SELECT DISTINCT s.NAME
FROM STUDENT_SUBJECTS sb,
STUDENTS s,
SUBJECTS b
WHERE s.ID = sb.STUDENT_ID
AND b.ID = sb.SUBJECT_ID
AND sb.MARK >= 50
but this get students who have any subject that is bigger than or equal to 50
is there any way to get just the names of students who passed all subjects?
This should works:
SELECT s.NAME
FROM STUDENT_SUBJECTS sb,
STUDENTS s,
SUBJECTS b
WHERE s.ID = sb.STUDENT_ID
AND b.ID = sb.SUBJECT_ID
GROUP BY S.NAME
HAVING MIN(SB.MARK)>=50
In case exists two students with same name just group by id also:
SELECT s.NAME
FROM STUDENT_SUBJECTS sb,
STUDENTS s,
SUBJECTS b
WHERE s.ID = sb.STUDENT_ID
AND b.ID = sb.SUBJECT_ID
GROUP BY S.ID,S.NAME
HAVING MIN(SB.MARK)>=50