SQL Query: Retrieve list which matches criteria - sql

Sorry, I couldn't think of a better heading (or anything that makes sense).
I have been trying to write a SQL query where I can retrieve the names of student who have the same level values as student Jaci Walker.
The format of the table is:
STUDENT(id, Lname, Fname, Level, Sex, DOB, Street, Suburb, City, Postcode, State)
So I know the Lname (Walker) and Fname (Jaci) and I need to find the Level of Jaci Walker and then output a list of names with the same Level.
--Find Level of Jaci Walker
SELECT S.Fname, S.Name, S.Level
FROM Student S
WHERE S.Fname="Jaci" AND S.Lname="Walker"
GROUP BY S.Fname, S.Lname, S.Level;
I have figured out how to retrieve the Level of Jaci Walker, but don't know how to apply that to another query.
Thankyou to everyone for your help,
I'm just stuck on one little bit when adding the rest of the query into it.
https://www.dropbox.com/s/3ws93pp1vk40awg/img.jpg
SELECT S.Fname, S.LName
FROM Student S, Enrollment E, CourseSection CS, Location L
WHERE S.S_id = E.S_id
AND E.C_SE_ID = CS.C_SE_id
AND L.Loc_id = CS.Loc_ID
AND S.S_Level = (SELECT S.S_Level FROM Student S WHERE S.S_Fname = "Jaci" AND S.S_Lname = "Walker")
AND CS.C_SE_id = (SELECT CS.C_SE_id FROM CourseSection CS WHERE ?)
AND L.Loc_id = (SELECT L.Blodg_code FROM Location L WHERE L.Blodg_code = "BG");

try this :
SELECT S.Fname, S.Name, S.Level
FROM Student S
WHERE S.Level =
(SELECT Level
FROM Student
WHERE Fname="Jaci" AND Lname="Walker"
)
but you got to be sure to have only 1 student called Jaci Walker ...

You can re-use your query as a subquery to find other entries with the same Level.
SELECT Fname, Name
FROM Student
WHERE Level = (
SELECT Level FROM Student S WHERE S.Fname="Jaci" AND S.Lname="Walker")

You don't need to group your result.
Try this
Select Fname,Lname from Student
where Level=(Select Level
from Student
where Fname='Jaci' AND Lname='Walker' );

Try this:
SELECT S.Fname, S.Name, S.Level FROM Student s
WHERE Level =
(SELECT TOP 1 Level FROM Student WHERE Fname = "Jaci" and Lname = "Walker")
If you don't use TOP 1, this query will fail if you have more than one "Jaci Walker" in your data.

I don't think you need group by and all for same...
Simply,
SELECT S.Fname, S.Name, S.Level FROM Student S WHERE S.LEVEL LIKE (SELECT LEVEL FROM STUDENT WHERE Fname="Jaci" AND Lname="Walker");
Are you looking for same?

Related

'ALL' concept in SQL queries

Relational Schema:
Students (**sid**, name, age, major)
Courses (**cid**, name)
Enrollment (**sid**, **cid**, year, term, grade)
Write a SQL query that returns the name of the students who took all courses.I'm not sure how I capture the concept of 'ALL' in a SQL query.
EDIT:
I want to be able write it without aggregation as I want to use the same logic for writing the query in relational algebra as well.
Thanks for the help!
One way of writing such queries is to count the number of course and number of courses each student took, and compare them:
SELECT s.*
FROM students s
JOIN (SELECT sid, COUNT(DISTINCT cid) AS student_courses
FROM enrollment
GROUP BY sid) e ON s.sid = e.sid
JOIN (SELECT COUNT(*) AS cnt
FROM courses) c ON cnt = student_cursed
This gives course combinations that are possible but haven't been taken...
SELECT s.sid, c.cid FROM students CROSS JOIN courses
EXCEPT
SELECT sid, cid FROM enrollment
So, you can then do the same with the student list...
SELECT sid FROM students
EXCEPT
(
SELECT DISTINCT
sid
FROM
(
SELECT s.sid, c.cid FROM students CROSS JOIN courses
EXCEPT
SELECT sid, cid FROM enrollment
)
AS not_enrolled
)
AS slacker_students
I don't like it, but it avoids aggregation...
SELECT *
FROM Students
WHERE NOT EXISTS (
SELECT 1 FROM Courses
LEFT OUTER JOIN Enrollment ON Courses.cid = Enrollment.cid
AND Enrollment.sid = Students.sid
WHERE Enrollment.sid IS NULL
)
btw. names of tables should be in singular form, not plural

SQL Query - Unsure How to Fix Logical Error

Edit: Sorry! I am using Microsoft SQL Server.
For clarification, you can have a department named "x" with a list of jobs, a department named "y" with a different list of jobs, etc.
I also need to use >= ALL instead of TOP 1 or MAX because I need it to return more than one value if necessary (if job1 has 20 employees, job2 has 20 employees and they are both the biggest values, they should both return).
In my query I'm trying to find the most common jobTitle and the number of employees that work under this jobTitle, which is under the department 'Research and Development'. The query I've written consists of joins to be able to return the necessary data.
The problem I am having is with the WHERE statement. The HAVING COUNT(JobTitle) >= ALL is finding the biggest number of employees that work under a job, however the problem is that my WHERE statement is saying the Department must be 'Research and Development', but the job with the most amount of employees comes from a different department, and thus the output produces only the column names and nothing else.
I want to redo the query so that it returns the job with the largest amount of employees that comes from the Research and Development department.
I know this is probably pretty simple, I'm a noob :3 Thanks a lot for the help!
SELECT JobTitle, COUNT(JobTitle) AS JobTitleCount, Department
FROM HumanResources.Employee AS EMP JOIN
HumanResources.EmployeeDepartmentHistory AS HIST
ON EMP.BusinessEntityID = HIST.BusinessEntityID JOIN
HumanResources.Department AS DEPT
ON HIST.DepartmentID = DEPT.DepartmentID
WHERE Department = 'Research and Development'
GROUP BY JobTitle, Department
HAVING COUNT(JobTitle) >= ALL (
SELECT COUNT(JobTitle) FROM HumanResources.Employee
GROUP BY JobTitle
)
If you only want one row, then a typical method is:
SELECT JobTitle, COUNT(*) AS JobTitleCount
FROM HumanResources.Employee AS EMP JOIN
HumanResources.EmployeeDepartmentHistory AS HIST
ON EMP.BusinessEntityID = HIST.BusinessEntityID JOIN
HumanResources.Department AS DEPT
ON HIST.DepartmentID = DEPT.DepartmentID
WHERE Department = 'Research and Development'
GROUP BY JobTitle
ORDER BY COUNT(*) DESC
FETCH FIRST 1 ROW ONLY;
Although FETCH FIRST 1 ROW ONLY is the ANSI standard, some databases spell it LIMIT or even SELECT TOP (1).
Note that I removed DEPARTMENT both from the SELECT and the GROUP BY. It seems redundant.
And, if I had to guess, your query is going to overstate results because of the history table. If this is the case, ask another question, with sample data and desired results.
EDIT:
In SQL Server, I would recommend using window functions. To get the one top job title:
SELECT JobTitle, JobTitleCount
FROM (SELECT JobTitle, COUNT(*) AS JobTitleCount,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as seqnum
FROM HumanResources.Employee AS EMP JOIN
HumanResources.EmployeeDepartmentHistory AS HIST
ON EMP.BusinessEntityID = HIST.BusinessEntityID JOIN
HumanResources.Department AS DEPT
ON HIST.DepartmentID = DEPT.DepartmentID
WHERE Department = 'Research and Development'
GROUP BY JobTitle
) j
WHERE seqnum = 1;
To get all such titles, when there are duplicates, use RANK() or DENSE_RANK() instead of ROW_NUMBER().
with employee_counts as (
select
hist.DepartmentID, emp.JobTitle, count(*) as cnt,
case when dept.Department = 'Research and Development' then 1 else 0 end as is_rd,
from HumanResources.Employee as emp
inner join HumanResources.EmployeeDepartmentHistory as hist
on hist.BusinessEntityID = emp.BusinessEntityID
inner join HumanResources.Department as dept
on dept.DepartmentID = hist.DepartmentID
group by
hist.DepartmentID, emp.JobTitle
)
select * from employee_counts
where is_rd = 1 and cnt = (
select max(cnt) from employee_counts
/* where is_rd = 1 */ -- ??
);

SQL query, NOT EXISTS

Hi I am evaluating a SQL query with the following schema:
Student(sid, name, age, gender, dept, GPA)
Faculty(fid, name, age, office, dept)
Course(cid, name, description)
Teach(fid, cid, term)
Enrollment(sid, cid, term, grade, final grade)
The question to base query on is:
Find the students who took at least one course with Tom
The query looks like:
SELECT *
FROM Student S1
WHERE NOT EXISTS (SELECT E1.cid
FROM Enrolment E1, Student S2
WHERE E1.sid = S2.sid
AND S2.name = 'Tom'
MINUS
SELECT E2.Cid
FROM E2 Enrollment
WHERE E2.sid = S1.sid)
This query is not making sense to me because to me it seems like the NOT EXISTS condition would only select a student when the subquery is empty, and this would only occur in the case when student s1 has taken all the same courses as Tom. However the question is to find the students who took at least one course, not all courses as Tom. Maybe I am interpreting wrong but I could use some clarification.
Try this
DECLARE #TomID nvarchar(MAX)
set #TomID = (SELECT TOP 1 ID from Students where Name = 'Tom')
select distinct E1.SID from Enrollment E1
where E1.SID != #TomID AND E1.CID in
(select distinct E.CID from Enrollment E
where E.SID=#TomID)

SQL select, 3 tables

How can I use select if I have 3 tables?
Tables:
school_subject(ID_of_subject, workplace, name)
student(ID_of_student, firstName, surname, adress)
writing(ID_of_action, ID_of_sbuject, ID_of_student)
I want to write the student's name and surname (alphabetically) who have workplace=home.
Is this possible? And how to alphabetize?
SELECT s.firstName, s.surname
FROM student S, school_subject P, writing Z
WHERE P.workplace = 'home'
AND P.ID_of_subject = Z.ID_of_subject
AND Z.ID_of_student = s.ID_of_student;
SELECT s.firstName, s.surname
FROM student S INNER JOIN writing Z
ON Z.ID_of_student = s.ID_of_student
INNER JOIN school_subject P
ON P.ID_of_subject = Z.ID_of_subject
WHERE P.workplace = 'home'
ORDER BY S.firstName, S.surname // Sort the list
To order alphabetically the result it is possible to use ORDER BY keyword. So your query becomes:
SELECT DISTINCT S.firstName, S.surname
FROM student S, school_subject P, writing Z
WHERE P.workplace = 'home' AND
P.ID_of_subject = Z.ID_of_subject AND
Z.ID_of_student = S.ID_of_student
ORDER BY S.surname, S.firstName
The DISTINCT keyword is necessary, because in writing table there are eventually more tuples given keys ID_of_subject and ID_of_student.
So this is necessary to avoid repeating firstName and surname many times.
Note that each student is identified by ID_of_student, not by firstName and surname, so as #danjok said use DISTINCT if you only want the name and surname.
If you want to select all students that satisfy your requirement (even if two or more students have the same firstName and surname), you should including ID_of_student on SELECT clause:
SELECT S.ID_of_student, S.firstName, S.surname
FROM student S
INNER JOIN writing W ON W.ID_of_student = S.ID_of_student
INNER JOIN school_subject P ON P.ID_of_subject = W.ID_of_subject
WHERE P.workplace = 'home'
ORDER BY S.firstName asc, S.surname asc

Limitations of GROUP BY

Disclaimer: I'm an SQL newb and this is for a class, but I could really use a poke in the right direction.
I've got these three tables:
student(_sid_, sname, sex, age, year, gpa)
section(_dname_, _cno_, _sectno_, pname)
enroll(_sid_, grade, _dname_, _cno_, _sectno_)
(primary keys denoted by underscores)
I'm trying to write an Oracle-compatible SQL query that returns a table with the student's name (student.sname) that has the highest gpa in each section (that's including section.cno and section.sectno) as well as all the other attributes from section.
I've managed to use an aggregate query and GROUP BY to get the maximum GPA for each section:
SELECT MAX(s.gpa), e.cno, e.sectno
FROM enroll e,
student s
WHERE s.sid = e.sid
GROUP BY e.cno, e.sectno
Let alone the other section attributes, I can't even figure out how to tack on the student name (student.sname). If I add it to the SELECT clause, it has to be included in GROUP BY which messes up the rest of the query. If I use this entire query inside the WHERE or FROM clause of an outer query, I can only access the three fields in the table, which isn't that much use.
I know you can't give me the exact answer, but any hints would be appreciated!
Assuming Oracle 9i+, to get only one of the students with the highest GPA (in the event of ties) use:
WITH summary AS (
SELECT e.*,
s.name,
ROW_NUMBER() OVER(PARTITION BY e.cno, e.sectno
ORDER BY s.gpa DESC) AS rank
FROM ENROLL e
JOIN STUDENT s ON s.sid = e.sid)
SELECT s.*
FROM summary s
WHERE s.rank = 1
Non CTE equivalent:
SELECT s.*
FROM (SELECT e.*,
s.name,
ROW_NUMBER() OVER(PARTITION BY e.cno, e.sectno
ORDER BY s.gpa DESC) AS rank
FROM ENROLL e
JOIN STUDENT s ON s.sid = e.sid) s
WHERE s.rank = 1
If you want to see all students who tied for GPA, use:
WITH summary AS (
SELECT e.*,
s.name,
DENSE_RANK OVER(PARTITION BY e.cno, e.sectno
ORDER BY s.gpa DESC) AS rank
FROM ENROLL e
JOIN STUDENT s ON s.sid = e.sid)
SELECT s.*
FROM summary s
WHERE s.rank = 1
Hint: consider that there might be more than one student with the highest GPA in a class. An outer query only needs the three fields.
Here are some pointers :-
You are on the right track with your Group By query
This returns you the max GPA for each section based on the cno and sectno fields
Now, you have the Max GPA value for each cno and sectno combination.
Use this data that you have in like a reverse manner if u like to now find all the students matching these combination values.
HINT : Consider the results of your Group By query as a table and Use a INNER JOIN
Even If it is possible that there is more than 1 students for the same max GPA, you will still get them all
Hope this helps!!
This should give you what you are looking for. See Oracle's RANK() function for details on how the GPAs are ranked from highest to lowest by section.
Requirements:
Return a table with the student's name (student.sname) that has the highest gpa in each section (section.cno and section.sectno) as well as all the other attributes from section.
SELECT * FROM
(
SELECT
s.sname,
s.gpa,
sec.dname,
sec.cno,
sec.sectno,
sec.pname,
/* for each "sec.cno, sec.sectno", this will rank each GPA in order from highest to lowest. Ties will have the same rank. */
RANK() OVER(PARTITION BY sec.cno, sec.sectno ORDER BY s.gpa DESC) as r_rank
FROM
enroll e,
student s,
section sec
WHERE
/* join enroll with student */
s.sid = e.sid
/* join section with enroll */
AND sec.dname = e.dname
AND sec.cno = e.cno
AND sec.sectno = e.sectno
)
WHERE r_rank = 1 /* this returns only the highest GPA (maybe multiple students) for each "sec.cno, sec.sectno" combination */
;
Note: If you do not want ties, change RANK() to ROW_NUMBER()
Maybe shortest:
SELECT DISTINCT e.cno, e.sectno , e...,
FIRST_VALUE(s.sname) OVER
(PARTITION BY e.cno, e.sectno ORDER BY s.gpa DESC)
FROM enroll e,
student s
WHERE s.sid = e.sid
Or
SELECT A.*
FROM
( SELECT s._sid, s.sname, e.cno, e.sectno ,..., s.gpa
MAX(s.gpa) OVER (PARTITION BY e.cno, e.sectno) AS maxgpa
FROM enroll e,
student s
WHERE s.sid = e.sid
) A
WHERE A.maxgpa = A.gpa
Although this is answered long back but still I want to present a beautiful explanation , this is very useful for newbie. Also Introduction to SQL has such rules.