What is wrong with this database query? - sql

I have the following tables in a database (i'll only list the important attributes):
Person(ssn,countryofbirth)
Parents(ssn,fatherbirthcountry)
Employment(ssn, companyID)
Company(companyID, name)
My task is this: given fatherbirthcountry as input, output the names of companies where persons work whose countryofbirth match the fatherbirthcountry input.
I pretend that the fatherbirthcountry is Mexico and do this:
SELECT name
FROM Company
WHERE companyid = (SELECT companyid
FROM Employment
WHERE ssn = (SELECT ssn
FROM Person
WHERE countryofbirth = 'Mexico');
but it is giving me an error:
>Scalar subquery is only allowed to return a single row.
am I completely off track? Can anybody please help?

The problem is that your subqueries are returning multiple results, so you have to use where in vs. =.
Change where ssn = to where ssn in, and where companyid = to where companyid in.

try using the IN keyword not '='.
try changing your query to this
SELECT name
FROM Company
WHERE companyid IN (SELECT companyid
FROM Employment
WHERE ssn IN (SELECT ssn
FROM Person
WHERE countryofbirth = 'Mexico');

Use:
SELECT c.name
FROM COMPANY c
JOIN EMPLOYMENT e ON e.companyid = c.companyid
JOIN PERSON p ON p.ssn = e.ssn
AND p.countryofbirth = 'Mexico'

You should use In in the where condition since the (SELECT ssn
FROM Person
WHERE countryofbirth = 'Mexico'); may return multiple ssn values.
SELECT name
FROM Company
WHERE companyid = (SELECT companyid
FROM Employment
WHERE ssn IN (SELECT ssn
FROM Person
WHERE countryofbirth = 'Mexico');

Try using IN instead of =
When you write:
select a from T where a = ( select....)
The sub-query must return a single value. In case if it returns multiple values, you get your error.
To solve this we use the IN operator which allows the sub-query to return a set of value (>=0) and your where condition succeeds if a equals any one of those values.
select a from T where a IN ( select....)

See if this works
SELECT c.Name FROM PERSON p
LEFT JOIN Employment e ON p.ssn=e.ssn
LEFT JOIN Company c ON
e.CompanyID=c.CompanyID WHERE
p.countryofbirth=

The error is due to the fact that the one of the two subqueries are returning multiple rows. I would think it likely that you have multiple people born in Mexico for example.
Select Name
From Companies
Where Exists(
Select 1
From Employment
Join Person
On Person.SSN = Employment.SSN
Join Parents
On Parents.SSN = Person.SSN
Where Parents.FatherBirthCountry = Person.CountryOfBirth
And Parents.FatherBirthCountry = #InputParam
And Employment.CompanyId = Companies.CompanyId
)

Ideally use the answer from OMG Ponies using JOINs.
But if you do not like JOINs for whatever reason, then TOP 1 should do the trick for you:
SELECT name
FROM Company
WHERE companyid =(SELECT TOP 1 companyid
FROM Employment
WHERE ssn = ( SELECT TOP 1 ssn
FROM Person
WHERE countryofbirth = 'Mexico');

Related

SELECT and JOIN to return only one row for each employee

I have a user table that stores the employeeId, lastName, firstName, department, hire date and mostRecentLogin.
I have another table that stores the employeeId, emailAddress.
The emailAddress table can have multiple rows for an employee if they have multiple email addresses.
I'm trying to return results that only show one row for each employee. I don't care which email address, just as long as it only picks one.
But all the queries I've tried always return all possible rows.
Here is my most recent attempt:
select *
from EmployeeInfo i
left join EmployeeEmail e ON i.employeeId = e.employeeId
where i.hireDate = 2015
and employeeId IN (
SELECT MIN(employeeId)
FROM EmployeeInfo
GROUP BY employeeId
)
But then again, this returns all possible rows.
Is there a way to get this to work?
Use a sub-query instead of a join:
select *
, (select top 1 E.EmailAddress from EmplyeeEmail E where E.employeeId = I.employeeId)
from EmployeeInfo I
where I.hireDate = 2015;
Note: If you change your mind and decide you do have a preference as to which email address is returned then just add an order by to the sub-query - otherwise it is truly unknown which one you will get.
This should work.
SELECT *
FROM EmployeeInfo
Left JOIN EmployeeEmail
ON EmployeeInfo.employeeId = EmployeeEmail.employeeId
WHERE EmployeeInfo.hireDate = '2015'
GROUP BY EmployeeInfo.employeeId;

PostgreSQL connecting two tables with 2 common attributes on Query

I have 2 entities; castfilm and staff in my erd that have a common attribute employeeid. Now, I wanted to Query in postgre to show the attributes that are related to the specific person that has an employeeid no. 307 where his fname and lname is David and Kaye respectively.
My erd looks like this: enter image description here
The attributes that I wanted to show are fname, lname, castrole, filmtitle, filmgenre, filmyear, companyname, staffposition, artistname and soundtitle that are related to the film of the employee named David Kaye
Here's my query:
SELECT distinct fname,lname,castrole, filmtitle, filmgenre, filmyear, companyname
FROM employee,
castfilm,
film,
production,
company
WHERE employee.employeeid = 307
AND employee.employeeid = castfilm.employeeid
AND castfilm.filmid = film.filmid
AND production.companyid = company.companyid
AND production.filmid = film.filmid
[output][2]
How do I show the other staffs and casts that are related to the specific movie that David Kaye is on?
You can use EXISTSstatement:
The argument of EXISTS is an arbitrary SELECT statement, or subquery.
The subquery is evaluated to determine whether it returns any rows. If
it returns at least one row, the result of EXISTS is "true"; if the
subquery returns no rows, the result of EXISTS is "false".
In the subquery you should check if for given filmid there exists an employeeid in castfilm. And this employeeid should be 307.
Like this:
SELECT distinct fname,lname,castrole, filmtitle, filmgenre, filmyear, companyname
FROM employee,
castfilm,
film,
production,
company
WHERE employee.employeeid = castfilm.employeeid
AND castfilm.filmid = film.filmid
AND production.companyid = company.companyid
AND production.filmid = film.filmid
AND EXISTS (
SELECT
employeeid
FROM
castfilm
WHERE
filmid = film.filmid AND
employeeid = 307
);
Please, note that film.filmid is used in the subquery.
Furthermore, as #jarlh noted, you could use JOIN syntax, like this:
SELECT
fname,
lname,
castrole,
filmtitle,
filmgenre,
filmyear,
companyname
FROM
employee
JOIN castfilm
ON (employee.employeeid = castfilm.employeeid)
JOIN film
ON (castfilm.filmid = film.filmid)
JOIN production
ON (film.filmid = production.filmid)
JOIN company
ON (production.companyid = company.companyid)
WHERE
EXISTS (
SELECT
employeeid
FROM
castfilm
WHERE
filmid = film.filmid AND
employeeid = 307
);

SQL Select entries where none of the entities have a value in a particular column

I have a table of data which has students and their subject results in it. The students will appear multiple times, once for each subject they have a result for.
**tableID,studentID,lastName,firstName,subject,grade**
1,1a,Student1,Name1,English,A
2,1a,Student1,Name1,Maths,A
3,1a,Student1,Name1,Science,A
4,2a,Student2,Name2,English,A
5,2a,Student2,Name2,Maths,B
6,2a,Student2,Name2,Science,A
7,3a,Student3,Name3,English,A
8,3a,Student3,Name3,Maths,A
Using Microsoft Access SQL, how can I select only the students who have received an A for all of their subjects? E.g. In the above table, I only want to select all instances of Student1 and Student3, I don't want Student2 as they have not received all A's.
Get all students with grade A except students with any other grade
SELECT
studentID,lastName,firstName
FROM
(SELECT
studentID,lastName,firstName
FROM
result
WHERE
grade = 'A'
GROUP BY
studentID,lastName,firstName) GradeA
LEFT OUTER JOIN
(SELECT
studentID,lastName,firstName
FROM
result
WHERE
grade <> 'A'
GROUP BY
studentID,lastName,firstName) GradeOther
ON GradeA.studentId = GradeOther.StudentID AND GradeA.LAstName = GradeOther.LastName AND GradeA.FirstName = GradeOther.FirstName
WHERE
GradeOther.StudentID IS NULL
One way is using GROUP BY and HAVING:
select StudentId
from t
group by StudentId
having max(grade) = min(grade) and max(grade) = 'A';
I was able to get the results I want by using a sub-query:
SELECT studentID, lastName, firstName
FROM table
WHERE grade = "A"
AND studentID NOT IN (SELECT studentID FROM table WHERE grade <> "A" GROUP BY studentID)
GROUP BY studentID, lastName, firstName
This seems to exclude all students who received a result other than an A.

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

SQL query for finding row with same column values that was created most recently

If I have three columns in my MySQL table people, say id, name, created where name is a string and created is a timestamp.. what's the appropriate query for a scenario where I have 10 rows and each row has a record with a name. The names could have a unique id, but a similar name none the less. So you can have three Bob's, two Mary's, one Jack and 4 Phil's.
There is also a hobbies table with the columns id, hobby, person_id.
Basically I want a query that will do the following:
Return all of the people with zero hobbies, but only check by the latest distinct person created, if that makes sense. Meaning if there is a Bob person that was created yesterday, and one created today.. I only want to know if the Bob created today has zero hobbies. The one from yesterday is no longer relevant.
select pp.id
from people pp, (select name, max(created) from people group by name) p
where pp.name = p.name
and pp.created = p.created
and id not in ( select person_id from hobbies )
SELECT latest_person.* FROM (
SELECT p1.* FROM people p1
WHERE NOT EXISTS (
SELECT * FROM people p2
WHERE p1.name = p2.name AND p1.created < p2.created
)
) AS latest_person
LEFT OUTER JOIN hobbies h ON h.person_id = latest_person.id
WHERE h.id IS NULL;
Try This:
Select *
From people p
Where timeStamp =
(Select Max(timestamp)
From people
Where name = p.Name
And not exists
(Select * From hobbies
Where person_id = p.id))