SQL - Query using Join / Having / Subquery - How - sql

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

Related

How to find people in a database who live in the same cities?

I'm new to SQL, and I'm asking for help in an apparently easy question, but it gets cumbersome in my mind.
I have the following table:
ID NAME CITY
---------------------
1 John new york
2 Sam new york
3 Tom boston
4 Bob boston
5 Jan chicago
6 Ted san francisco
7 Kat boston
I want a query that returns all the people who live in a city that another person registered in the database also lives in.
The answer, for the table I showed above, would be:
ID NAME CITY
---------------------
1 John new york
2 Sam new york
3 Tom boston
4 Bob boston
7 Kat boston
This is really a two part question:
What cities have more than one user located in them?
What users live in that subset of cities?
Let's answer it in two parts. Let's also make the simplifying assumption (not stated in your question) that the Users table has only one entry per user per city.
To find cities with more than one user:
SELECT City FROM Users GROUP BY City HAVING COUNT(*) > 1
Now, let's find all the users for those cities:
SELECT ID, User, City FROM Users
WHERE City IN (SELECT City FROM Users GROUP BY CITY HAVING COUNT(*) > 1)
I would use EXISTS :
SELECT t.*
FROM table t
WHERE EXISTS (SELECT 1 FROM table t1 WHERE t1.city = t.city AND t1.name <> t.name);
To avoid a correlated subquery which leads to a nested loop, you could perform a self join:
SELECT id, name, city
FROM persons
JOIN (SELECT city
FROM persons
GROUP BY city HAVING count(*) > 1) AS cities
USING (city);
This might be the most performant solution.
This will give you the rows that have the same city more than 1 time:
SELECT persons.*
FROM persons
WHERE (SELECT COUNT(*) FROM persons AS p GROUP BY CITY HAVING p.CITY = persons.CITY) > 1
This is just a different flavor from the others that have posted.
SELECT ID,
name,
city
FROM (SELECT DISTINCT
ID,
name,
city,
COUNT(1) OVER (PARTITION BY city) AS cityCount
FROM table) t
WHERE cityCount > 1
This can be expressed many ways. Here is one possible way:
select * from persons p
where exists (
select 1 from persons p2
where p2.city = p.city and p2.name <> p.name
)

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

Query Subquery Results in 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');

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)

how to avoid duplicate on Joining two tables

Student Table
SID Name
1 A
2 B
3 C
Marks Table
id mark subject
1 50 physics
2 40 biology
1 50 chemistry
3 30 mathematics
SELECT distinct(std.id),std.name,m.mark, row_number() over() as rownum FROM
student std JOIN marks m ON std.id=m.id AND m.mark=50
This result is 2 times A even after using disticnt . My expected result will have only one A. if i remove row_number() over() as rownum its working fine. Why this is happening ? how to resolve. AM using DB2!!
There are two rows in marks Table with id = 1 and mark = 50.. So you will get two rows in the output for each row in student table...
If you only want one, you have to do a group By
SELECT std.id, std.name, m.mark, row_number()
over() as rownum
FROM student std
JOIN marks m
ON m.id=std.id AND m.mark=50
Group By std.id, std.name, m.mark
Now that you've clarified your question as:
I want to find all students with a mark of 50 in at least one subject. I would use the query:
SELECT student.id, '50'
FROM student
WHERE EXISTS (SELECT 1 FROM marks WHERE marks.id = student.id AND marks.mark = 50)
This also gives you flexibility to change the criteria, e.g. at least one mark of 50 or less.
Similar to Charles answer, but you always want to put the predicate (mark=50) in the WHERE clause, so you're filtering before joining. If this is just homework it might not matter but you'll want to remember this if you ever hit any real data.
SELECT std.sid,
std.name,
m.mark,
row_number() over() AS rownum
FROM student std
JOIN marks m
ON std.sid=m.id
WHERE m.mark=50
GROUP BY std.sid, std.name, m.mark