Query Subquery Results in SQL - sql

I am relatively new to SQL and I may be over thinking it but given the following table.I want a list of all students that are enrolled in Chemistry but not Mathematics.So given the data I would have Student 1.
Student_ID Subject
1 Chemistry
2 Mathematics
2 Chemistry
3 History
Here's what I tried
SELECT Student_ID
FROM Student
WHERE (Subject = 'Chemistry') AND (Subject <> 'Mathematics')
Perhaps I need to group somewhere because rows and returned that exist for both criteria.

Here's one option using conditional aggregation:
select student_id
from student
group by student_id
having max(case when subject = 'Chemistry' then 1 end) = 1 and
max(case when subject = 'Mathematics' then 1 else 0 end) != 1
And here's another option using not exists:
select student_id
from student s
where subject = 'Chemistry' and not exists (
select 1
from student s2
where s.student_id = s2.student_id and st.subject = 'Mathematics'
)

You can use where and not in
select Student_ID
from Student
where Subject = 'Chemistry'
and Student_ID NOT IN ( select Student_ID from Student where subject ='Mathematics');

Related

How to retrieve latest instance using IF else condition in sqldeveloper

I have one student table which is given below
ROLL | STUDENT | SUBJECT | VERSION_ID |
1 A M 1
2 B M 2
3 C M 3
4 B S 1
5 D S 2
6 E E 1
7 F G 1
If there is atleast one record for SUBJECT='M' then retrieve latest record for SUBJECT 'M' based on version id.
ELSIF If there is atleast one record for SUBJECT='S' then retrieve latest record for SUBJECT 'S' based on version id.
ELSIF If there is atleast one record for SUBJECT='E' then retrieve latest record for SUBJECT 'E' based on version id.
Else DO NOT fetch any record.
Based on above condition I am trying to write a query or view which can satisfy all above condition and and give below output. Please suggest me possible solutions to get this.
Output
ROLL | STUDENT | SUBJECT | VERSION_ID |
3 C M 3
I have tried to get desired output using below query but I encountered with error. Please help me to solve this query with better performance.
SELECT * from ( IF EXISTS(select subject from STUDENT where SUBJECT='M') then
select * from STUDENT S1
inner join
(select S2.ROLL from STUDENT S2
where
S2.ROLL=(select S3.ROLL from STUDENT S3
where S3.SUBJECT='M'
and S3.VERSION_ID=(select MAX(S4.VERSION_ID) from STUDENT S4
GROUP BY S4.SUBJECT
HAVING S4.SUBJECT='M'))) S5
ON
S1.ROLL=S5.ROLL
ELSIF EXISTS(select subject from STUDENT where SUBJECT='S') then
select * from STUDENT S1
inner join
(select S2.ROLL from STUDENT S2
where
S2.ROLL=(select S3.ROLL from STUDENT S3
where S3.SUBJECT='S'
and S3.VERSION_ID=(select MAX(S4.VERSION_ID) from STUDENT S4
GROUP BY S4.SUBJECT
HAVING S4.SUBJECT='S'))) S5
ON
S1.ROLL=S5.ROLL
ELSIF EXISTS(select subject from STUDENT where SUBJECT='E') then
select * from STUDENT S1
inner join
(select S2.ROLL from STUDENT S2
where
S2.ROLL=(select S3.ROLL from STUDENT S3
where S3.SUBJECT='E'
and S3.VERSION_ID=(select MAX(S4.VERSION_ID) from STUDENT S4
GROUP BY S4.SUBJECT
HAVING S4.SUBJECT='E'))) S5
ON
S1.ROLL=S5.ROLL
ELSE
SELECT * FROM STUDENT WHERE 1=2);
TABLE CREATION:
Create table student (ROLL number,STUDENT varchar2(20),SUBJECT varchar2(20),VERSION_ID number );
Insert Data:
insert into STUDENT values(1,'A','M',1);
insert into STUDENT values(2,'B','M',2);
insert into STUDENT values(3,'C','M',3);
insert into STUDENT values(4,'B','S',1);
insert into STUDENT values(5,'D','S',2);
insert into STUDENT values(6,'E','E',1);
insert into STUDENT values(7,'F','G',1);
Thank You..
We can apply ROW_NUMBER to entire table, using the ordering logic you gave in your question. Subjects M, S, and E have priority, in that order, with the VERSION_ID breaking the tie. Rows with a subject not matching these three are assigned the lowest priority. Should there be no records with matching subjects, we filter out all records in the WHERE clause.
WITH cte AS (
SELECT ROLL, STUDENT, SUBJECT, VERSION_ID,
ROW_NUMBER() OVER (ORDER BY CASE WHEN SUBJECT = 'M' THEN 1
WHEN SUBJECT = 'S' THEN 2
WHEN SUBJECT = 'E' THEN 3
ELSE 4 END, VERSION_ID DESC END) rn
)
SELECT ROLL, STUDENT, SUBJECT, VERSION_ID
FROM cte
WHERE rn = 1 AND SUBJECT IN ('M', 'S', 'E')
You want right answer right? Try this :
SELECT TOP 1 #student.*
FROM student
JOIN (
SELECT MAX(VERSION_ID) VERSION_ID,SUBJECT
FROM student
WHERE SUBJECT = 'M'
GROUP BY SUBJECT
UNION ALL
SELECT MAX(VERSION_ID),SUBJECT
FROM student
WHERE SUBJECT = 'S'
GROUP BY SUBJECT
UNION ALL
SELECT MAX(VERSION_ID),SUBJECT
FROM student
WHERE SUBJECT = 'E'
GROUP BY SUBJECT
UNION ALL
SELECT MAX(VERSION_ID),SUBJECT
FROM student
WHERE SUBJECT = 'G'
GROUP BY SUBJECT
UNION ALL
SELECT NULL,NULL
) AS Dtls
ON (student.SUBJECT = Dtls.SUBJECT
AND student.VERSION_ID = Dtls.VERSION_ID)

take Duplicated ID's out and Identify a new columns

I Joined 6 table together to gather all information that I need.
I want all Id's, Names, Birthdays, and Ethnicity.
Some Ids have 2 or more Ethnicity and that will cause a id be duplicated.
I am thinking of writing a sub query or can I just use a case statement since I have tried case statement before and works for another case but I can not apply it in this case.
what I have is:
ID NAME Birthdays Ethnicity
4000 Pedram 11/11/1999 Middle East
4001 Carlos 11/11/1920 Spanish
4001 Carlos 11/11/1920 Native American
4002 Asia 11/22/1986 Polish
4002 Asia 11/22/1986 Native American
4002 Asia 11/22/1986 White/caucassian
I want to say if any Id duplicated and ethnicity is different <> just give me this:
ID NAME Birthdays Ethnicity
4000 Pedram 11/11/1999 Middle East
4001 Carlos 11/11/1920 Multiracial
4002 Asia 11/22/1986 multiracial
PS : ethnicity is in a different table and I joined it to Person_table
PS : to be able to join ethnicity table to Person_table I needed to join 3 more tables that have pr keys that can related to each other.
PS : I tried CASE WHEN Count (Id) > 1 THEN 'Multiracial' ELSE Ethnicity END AS Ethnicity_2
and it Identify all ethnicity as Multiracial.
Any help Or thought will be appreciate.
You can use this:
WITH CTE AS
(
SELECT *,
N = COUNT(*) OVER(PARTITION BY ID),
RN = ROW_NUMBER() OVER(PARTITION BY ID ORDER BY Ethnicity)
FROM dbo.YourTable
)
SELECT ID,
NAME,
Birthdays,
CASE WHEN N > 1 THEN 'Multiracial' ELSE Ethnicity END Ethnicity
FROM CTE
WHERE RN = 1;
This one might not be the most efficient but it works. Just substitute your derived table for t below:
SELECT DISTINCT t.id, t.name,
CASE WHEN cnt = 1 THEN ethnicity
ELSE 'Multiracial' END AS ethnicity
FROM t
INNER JOIN
(SELECT id, COUNT(DISTINCT ethnicity) AS cnt
FROM t
GROUP BY id) sub
ON t.id = sub.id
Tested here: http://sqlfiddle.com/#!9/7473f/6
SELECT
id, name, Birthdays,
IIF(COUNT(DISTINCT Ethnicity) > 1, 'Multiracial', MIN(Ethnicity)) as Ethnicity
FROM
Table
GROUP BY
id, name, Birthdays
SELECT
id, name, Birthdays,
CASE WHEN COUNT(DISTINCT Ethnicity) > 1 THEN 'Multiracial' ELSE MIN(Ethnicity) END as Ethnicity
FROM
Table
GROUP BY
id, name, Birthdays

SQL Query that summarizes students marks in a report card

Stupid simple question that I ended up spending 3.5 hours on. I'm running into a lot of syntax errors, so if anyone could help me answer this, I would learn a lot! Thank you so much!
I have 3 database tables:
Students Table
student_id, name
1, joe
2, jill
Courses Table
course_id, course_name
eng123, Engineering
stat111, Statistics
Marks Table
student_id, course_id, mark
1, stat111, 64
2, stat111, 90
1, eng123, 86
I need to write a single SQL query that will give me a summed up report card that looks like this:
student_id, student_name, eng123, stat 111
1, joe, 86, 64
2, jill, null, 90
---WHAT I EXPLORED:
I have looked into PIVOT, CASE and GROUP BY as my main leads, but I cannot put together the final pieces. My most promising query so far has been:
SELECT Students.student_id, Students.student_name,
CASE course_id WHEN 'eng123' THEN mark END as 'eng123',
CASE course_id WHEN 'stat111' THEN mark END as 'stat111'
FROM Students
INNER JOIN Marks
ON Students.student_id=Marks.student_id;
But that gives me the incorrect results of:
student_id, student_name, eng123, stat111
1, joe, null, 64
1, joe, 86, null
2, jill, null, 90
Your promising query is a stone's throw away from being what you want. You can simply modify it to GROUP BY the student_id and student_name (which should always pair the same together). Then take the sum of the marks. Note that I have added ELSE conditions to your CASE statements which assign a value of 0 (which therefore won't affect the sum aggregate).
SELECT Students.student_id, Students.student_name,
SUM(CASE course_id WHEN 'eng123' THEN mark ELSE 0 END) as 'eng123',
SUM(CASE course_id WHEN 'stat111' THEN mark ELSE 0 END) as 'stat111'
FROM Students
INNER JOIN Marks ON Students.student_id=Marks.student_id
GROUP BY Students.student_id, Students.student_name
Hi try using aggregate function MAX. try this:
WITH x AS (SELECT 1 AS student_id, 'joe' AS student_NAME FROM dual UNION ALL
SELECT 2 AS student_id, 'jill' AS student_NAME FROM dual),
y AS (SELECT 1 AS student_id , 'stat111' AS course_id, 64 AS mark FROM dual UNION ALL
SELECT 2 AS student_id , 'stat111' AS course_id, 90 AS mark FROM dual UNION ALL
SELECT 1 AS student_id , 'eng123' AS course_id, 86 AS mark FROM dual )
SELECT x.student_id, x.student_name,
MAX (CASE WHEN course_id = 'eng123' THEN mark END) eng123,
max(CASE WHEN course_id = 'stat111' THEN mark END) stat111
FROM x
INNER JOIN y
ON x.student_id=y.student_id
group by x.student_id, x.student_name;
select student_id,student_name,
max(case when course_id = 'eng123' then mark end) as eng123,
max(case when course_id = 'stat111' then mark end) as stat111
from (
SELECT Students.student_id as student_id,
Students.name as student_name,
Marks.course_id as course_id,
Marks.mark as mark
FROM Student Students
INNER JOIN Marks
ON Students.student_id=Marks.student_id)Z
group by student_id,student_name

How to query varchar and int

I have this table
name | prof |grade
------------------------
yossi math 100
tami math 70
yossi phisic 100
tami phisic 100
oren math 100
oren phisic 80
dor history 70
The query should return name of student which have grade 100 in math and phisic
The correct unswer is yossi
I used the following
SELECT name FROM [dbo].[Class_grade]
where prof in ('math', 'phisic') and grade = 100
but it returns more names why?
what is the right query?
Thanks
select name
from Class_Grade
where grade = 100 and
prof in ('math', 'phisic')
group by name
having count(distinct prof) = 2
Group by name and filter out the rows using having. Make sure you count the distinct occurrences of prof. In this case it is 2 because you have 2 values in your in clause.
Earlier i misquoted the Question
You can use Group by caluse
SELECT name FROM [Class_grade]
where prof in ('math', 'phisic') and grade = 100
group by (name)
having count(1)=2
SQL FIDDLE
Try this
SELECT name FROM Class_grade
WHERE prof IN ('math', 'phisic') AND grade=100
GROUP BY name
HAVING COUNT(name) > 1
group by and make suure you get two rows
SELECT name
FROM Class_grade
WHERE prof in ('math', 'phisic') and grade = 100
GROUP BY name
HAVING count(*) = 2
http://www.sqlfiddle.com/#!3/9308c/6
EDIT
you can grab distinct grades if the same student can have more than one 100% in a subject
SELECT name
FROM (
SELECT DISTINCT name, prof, grade
FROM Class_grade
WHERE prof in ('math', 'phisic') and grade = 100
) class_grade
GROUP BY name
HAVING count(*) = 2
http://www.sqlfiddle.com/#!3/79fd0/11
The OP did not state a few details:
What is the primary key on the table (i.e. can multiple lines exist for the same student for the same subject)
What is the expected result when there are multiple students who match the criteria.
If we assume that one student can have multiple grades in the same subject, many of the other answers would be wrong with the HAVING COUNT(*) clauses. Mikael Eriksson's answer satisfies that assumption, and it is possibly more performant than my below solution (though functionally different a bit too):
SELECT DISTINCT name FROM [dbo].[Class_grade] cg1
WHERE EXISTS (
SELECT 1 FROM [dbo].[Class_grade] cg2
WHERE cg2.name = cg1.name
AND cg2.prof = 'math'
AND cg2.grade = 100)
AND EXISTS (
SELECT 1 FROM [dbo].[Class_grade] cg2
WHERE cg2.name = cg1.name
AND cg2.prof = 'phisic'
AND cg2.grade = 100)
AND NOT EXISTS (
SELECT 1 FROM [dbo].[Class_grade] cg2
WHERE cg2.name = cg1.name
AND cg2.prof in ('math','phisic')
AND cg2.grade < 100)
The difference in the above code is that it will only select students who have only grade = 100 for the math and phisic subjects, even if they could have more than one grade per subject. See here.
select Class_grade.name
from Class_grade
inner join Class_grade Class_grade2
on Class_grade.Name = Class_grade2.name
where Class_grade.Prof = 'math' and Class_grade.grade = 100
and Class_grade2.prof = 'phisic' and Class_grade2.grade = 100
group by Class_grade.Name
try not to use distinct, its a poor coding
Try this:
SELECT DISTINCT name FROM [dbo].[Class_grade] where prof in ('math', 'phisic') and grade = 100
This would work as well:
SELECT DISTINCT name FROM [dbo].[Class_grade] where (prof = 'math' and grade = 100) OR (prof = 'phisics' and grade = 100)

SQL - Query using Join / Having / Subquery - How

Please suggest a sql query to fetch the
"list of students and corresponding subject in which student has same score , if the student has same score in more than one subject"
eg.
Student Subject Score
John Science 80
John Maths 80
John English 80
John French 80
Peter Science 85
Peter Maths 70
Peter English 70
Peter French 70
Mathews French 70
Expected Result :
John Science
John Maths
John English
John French
Peter Maths
Peter English
Peter French
tried -
select person , subject where person in
( select person , score , count(score) group by person , score having count(score) > 1 )
but in this "Peter Science" appears , which is not required.
Please assist.
Thanks
Try:
select person , subject
from table t
join
( select person , score , count(score)
from table
group by person , score having count(score) > 1
) foo
on t.person = foo.person
where t.person = foo.person
and t.score = foo.score
The reason your query didn't work (aside from not including a table):
Your inner query was selecting the students and scores that match your criteria. But your outer query was selecting everything for all students who matched the criteria, which is why "peter science" was being included. So, you needed to limit the results in your outer query to only those that match all of your criteria.
You should tell us what RDBMS are you using. But this should work everywhere:
select
t.*
from <tableName> t
join (
select
student, score
from <tableName>
group by
student, score
having count(*)>1
) x
on t.student=x.student
and t.score=x.score
Btw you're missing FROM <tableName> in your query.
Try with this :
select student, subject
from Person
where student + cast(score as varchar(20)) in
(
select student + cast(score as varchar(20)) from Person group by student,score having count(score) > 1
)
You can achieve this goal self-joining table like in example:
SELECT
t.Student,
t.Subject,
t.Score
FROM Table1 AS t
JOIN
(
SELECT Student, Score FROM Table1 GROUP BY Student, Score HAVING COUNT(*) > 1
) AS t1 ON t.Student = t1.Student AND t.Score = t1.Score
Full example with test data and created table you can find here:
http://sqlfiddle.com/#!2/61f02/8