I'm a database class practicing some SQL problems to sharpen my skills from my book. Here is the database schema I am using for reference to the problems (the solutions posted are correct, I just need some explanation to better understand them):
Q1: Retrieve the name of each employee who has a dependent with the same first name and is the same sex as the employee.
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
WHERE E.SSN IN (SELECT D.ESSN, FROM DEPENDENT AS D, WHERE E.FNAME = D.DEPENDENT_NAME AND D.SEX = E.SEX);
I’m a bit confused about this query. Shouldn’t the first FROM clause include “DEPENDENT AS D”? Why are we nesting this? I don’t see a reason why. Just seems to make things more complicated.
Q2: Retrieve the name of the employees who don’t have any dependents.
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
WHERE NOT EXISTS (SELECT *, FROM DEPENDENT AS D, WHERE D.ESSN = E.SSN);
Instead of using WHERE NOT EXISTS, can't we just use NULL?
Why use the “WHERE NOT EXISTS” clause? Can’t we just use NULL?
Q3: List the names of managers who have at least one dependent.
SELECT FNAME, LNAME
FROM EMPLOYEE
WHERE EXISTS(SELECT * FROM DEPARTMENT WHERE SSN = MGR_SSN)
WHERE EXISTS (SELECT * FROM DEPENDENT WHERE ESSN = SSN);
I honestly just don’t understand the rationale behind this query. I get it somewhat, but am having a hard time even explaining it to myself. Shouldn’t the FROM clause include DEPARTMENT and DEPENDENT? Why does it include just EMPLOYEE? If someone can walk me through this query step by step and explain me why we use those WHERE EXISTS clauses the way it’s being used, that would be really helpful.
Someone needs to teach that class about appropriate comma use.
Q1: No, you wouldn't want the first FROM to be on DEPENDENT, you are trying to select employee information, not dependents'. The subquery is used to find the essn (parent ssn) values for the dependents who have the same name and gender as their parent.
Edit: Upon further inspection of the Q1 query, it does seems rather convoluted; and almost definitely not the best way to go about getting that information. Leaving the subquery as it is, using an EXISTS instead of an IN would not suffice, as it is possible there is a dependent with the same name and gender as the correlated employee that is not actually the employee's dependent. If AND D.ESSN = E.SSN were added to the subquery's WHERE criteria, then EXISTS would be a valid choice. However, there is really no way to "un-correlate" the subquery that does not make it completely obvious that an INNER JOIN on the three fields involved would have been a better choice (outside of the rare cases where some nincompoop names more than one of their same-gendered dependents after themselves; in which case an INNER JOIN is still probably a better solution but now DISTINCT should be used in the SELECT.).
Q2: I'm not sure how you would plan to use NULL.
Q3: Again, you wouldn't (or at least don't need to) select FROM the other tables in the outer query because you are not needing data from them in the results. The two EXISTS conditions are (likely) using correlated subqueries (probably with poor efficiency/performance, JOINs are usually much faster); perhaps it would be more obvious written with aliases like this:
SELECT e.FNAME, e,LNAME
FROM EMPLOYEE AS e
WHERE EXISTS(SELECT * FROM DEPARTMENT AS d WHERE e.SSN = d.MGR_SSN)
AND EXISTS (SELECT * FROM DEPENDENT AS d WHERE d.ESSN = e.SSN);
(Edit: As another answerer noted; you can't have multiple WHERE's in (the same "level" of) a query.
or to translate it to English...
"Give me the first and last name of employees where there is a
department whose manager's ssn is the same as employee's ssn, and
there is a dependent whose parent's ssn is the same as the
employee's."
First one wouldnt run. Have to many , Should be:
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
WHERE E.SSN IN (SELECT D.ESSN
FROM DEPENDENT AS D
WHERE E.FNAME = D.DEPENDENT_NAME
AND D.SEX = E.SEX);
And if you ask if can be write as a JOIN, yes like this:
SELECT DISTINCT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
JOIN DEPENDENT D
ON E.SSN = D.ESSN
AND E.FNAME = D.DEPENDENT_NAME
AND D.SEX = E.SEX
Second one, again too many , and yes you can check for NULL but need to use LEFT JOIN
SELECT E.FNAME, E.LNAME
FROM EMPLOYEE AS E
LEFT JOIN DEPENDENT D
ON E.SSN = D.ESSN
WHERE D.ESSN IS NULL -- IF doesnt have any match, will only have a NULL row
Third One cant have two WHERE you need AND
SELECT FNAME, LNAME
FROM EMPLOYEE
WHERE EXISTS (SELECT * FROM DEPARTMENT WHERE SSN = MGR_SSN)
AND EXISTS (SELECT * FROM DEPENDENT WHERE ESSN = SSN);
First exists tell you that employee is also a manager. Second exists tell you that employee have a dependent
Related
I am trying to query this table:
https://www.w3resource.com/mysql-exercises/subquery-exercises/find-the-names-of-the-employees-who-have-a-manager-who-works-for-a-department-based-in-united-states.php
the question is:
Write a query to find the name (first_name, last_name) of the employees who have a manager and worked in a USA based department.
Apart from the solution that is given, I tried querying using Join and another by using Temporary Table. While these two queries return the same result but the Sub Query displays a few more rows. Why is that?
using join:
select
employees.MANAGER_ID,
employees.FIRST_NAME,
employees.LAST_NAME,
locations.COUNTRY_ID
from employees
inner join departments on
employees.DEPARTMENT_ID = departments.DEPARTMENT_ID
inner join locations on
departments.LOCATION_ID = locations.LOCATION_ID
where locations.COUNTRY_ID = 'US';
using Temporary Table:
create temporary table deptwithloc
select
departments.DEPARTMENT_ID,
departments.LOCATION_ID,
locations.COUNTRY_ID
from departments
left join locations
on departments.LOCATION_ID = locations.LOCATION_ID
where locations.COUNTRY_ID = 'US';
Subquery:
select
MANAGER_ID,
FIRST_NAME,
LAST_NAME
from employees
where MANAGER_ID
in (select
EMPLOYEE_ID
from employees
where DEPARTMENT_ID
in (select
DEPARTMENT_ID
from departments
where LOCATION_ID
in (select LOCATION_ID from locations where COUNTRY_ID='US' )));
Kindly help me to understand. New to database systems.
Your first query is just looking at ALL employees with offices in the US, not just the managers. Your second query is applying the condition where the MANAGER's department / location is tied to the U.S. Now, the wording of the question COULD be ambiguous on which "who" worked in the U.S... It could be a PERSON works in the U.S., but the manager is based in Canada, but per their sql answer, it is the MANAGER who works in the US.
Now, that being said, I like to do indentation on my queries to see all the pieces of what I'm looking for and how I get from point A - Z along the way. By indenting you see the relationships between the tables. So lets look at this a bit in reverse. You care about managers based out of the US. So, employee -> Department -> Location -> 'US'
Start by thinking of just the MANAGER, you dont need ALL employees (yet)
select distinct Manager_ID from Employees where Manager_ID > 0
Now, take it to the department associated with "US" location
select distinct
e.Manager_ID
from
employees e
JOIN departments d
on e.department_id = d.department_id
JOIN locations l
on d.location_id = l.locationID
AND l.country_id = 'US'
where
e.Manager_id > 0
Now, we have a finite list of distinct managers regardless of how many employees they have assigned to them. So now you can take THIS query as the primary and get any employees associated with them -- regardless of where the employee actually works -- such as remote employee working in different country than the manager.
select
e2.*
from
( select distinct
e.Manager_ID
from
employees e
JOIN departments d
on e.department_id = d.department_id
JOIN locations l
on d.location_id = l.locationID
AND l.country_id = 'US'
where
e.Manager_id > 0 ) JustMgrs
JOIN employees e2
on JustMgrs.Manager_id = e2.manager_id
So, the second query uses the first query as its basis of records. By wrapping that in the from with ( query ) alias, You can test each individual portion of a query and once it works, that can now be applied without a full write of a temporary table. I can just use the "alias" the rest of the way. So the "JustMgr" gives me only the managers ONCE regardless of how many employees. THEN it re-joins back to the employee table FOR THAT Manager who was in the qualified list in the 'US'.
Does that help clarify why? Does this option also help you see how one could partially dissect a question, get one part first, then go to the next.
FEEDBACK / CLARIFICATION
First, I corrected the query trying to add in the last/first name of the manager, that part was incorrect and probably skewed results but now is ok since updated.
To try and clarify on your comment to explain the second part. Think of it this way, do you understand completely the basis of the first query of getting only MANAGERS based in a U.S. location. By doing DISTINCT, you are getting only one instance of a manager no matter how many employees under them. Its like going into a room of all employees of a company and asking all managers to raise their hands and you get a few people. Now you ask them if the work in a US office to come up on stage and even less of those managers. So now you have your finite list.
Now you tell all employees working for one of those managers to come along too. The second query is using the entire first result set of unique managers and pulling only employees associated with them.
When doing queries, you can write one query and make sure everything works and put that into a "from" clause. By wrapping it within parenthesis and putting a result table/alias name, you can now use THAT as if it were an actual table. You can see this as I wrapped ( the entire manager query ) JustMgrs. So now that is an alias I can use for the join back to employees to only grab those employees under those managers. Does that help?
I was given the following question for an assignment
Write a SQL statement to show the sum of Hours Worked for each Type of OWNER but exclude services of employees who have Experience Level of Junior and exclude any Owner Type with Less Than Four members
I'm having a hard time figuring out how to accomplish the last part of the question. I believe that it would require counting a portion of a select statement but I'm not sure how to do that.
So far, this is what I have:
SELECT SUM(HoursWorked), OwnerType
FROM PROPERTY_SERVICE
JOIN EMPLOYEE ON EMPLOYEE.EmployeeID=PROPERTY_SERVICE.EmployeeID
JOIN OWNED_PROPERTY ON OWNED_PROPERTY.PropertyID = PROPERTY_SERVICE.PropertyID
INNER JOIN OWNER ON OWNER.OwnerID=OWNED_PROPERTY.OwnerID
WHERE NOT EMPLOYEE.ExperienceLevel='Junior'
*This is where I believe the solution should go*
GROUP BY OWNER.OwnerType;
Presumably, you just want a HAVING clause:
SELECT SUM(HoursWorked), o.OwnerType
FROM PROPERTY_SERVICE ps
JOIN EMPLOYEE e ON e.EmployeeID = ps.EmployeeID
JOIN OWNED_PROPERTY op ON op.PropertyID = ps.PropertyID
INNER JOIN OWNER o ON o.OwnerID = op.OwnerID
WHERE e.ExperienceLevel <> 'Junior'
GROUP BY o.OwnerType
HAVING COUNT(*) < 4
This excludes groups of rows having the same OwnerType that contain less than 4 rows. You might need to adjust the expression to your exact definition of a member.
Note that I added table aliases to the query to make it easier to read and write. I would also recommend prefixing column HoursWorked with the table it belongs to, to avoid ambiguity.
need some quick help.. i think i'm supposed to use INNER JOIN, but i'm not really sure.
List the employee ID and the full name of the employee who sold or packaged the
transactions placed by customer Benjamin Jones. Each employee should only show up
once in your results
employeeID, first and last are in the EMPLOYEES table
benjamin jones' memberID is 101 and in the MEMBERS table
memberID and transactionID and soldorpackagedby are in the TRANSACTIONS table
this is what I have to start it off but don't know how to finish it.
SELECT EmployeeID, First, Last
FROM Employees
WHERE MemberID = '101'
thanks!
Try -
SELECT employees.EmployeeID,
employees.First,
employees.Last
FROM employees
JOIN transactions ON employees.EmployeeID = transactions.SoldOrPackagedBy
WHERE transactions.memberID = 101
GROUP BY employees.EmployeeID;
Because you are using data from more than one table in this SQL statement you will need to specify which table a repeated field name such as EmployeeID will come from, hence employees.EmployeeID. employee. is probably not called for in front of First and Last but I tend to put it in anyway since the habit can help avoid bugs occurring when I am writing more complex statements.
JOIN without INNER, LEFT, RIGHT or FULL in front is the same as INNER JOIN (see https://www.w3schools.com/sql/sql_join.asp).
Since the Member's ID is already known and used in the Transactions table, and we are not being asked to refer to any other detail about the member here, then there is no need to link in the Members table.
Without GROUP BY the SELECT statement will return a record for every time an Employee packaged a Transaction for a Member. These details would be the same every time. Using GROUP BY would reduce this to just one instance of those details.
If you have any questions or comments, then please feel free to post a Comment accordingly.
Try this one
SELECT employees.EmployeeID,
employees.First,
employees.Last
FROM employees
JOIN transactions ON employees.EmployeeID = transactions.EmployeeID
WHERE transactions.memberID = 101
GROUP BY employees.EmployeeID, employees.First, employees.Last ;
I have this SQL query:
SELECT DISTINCT aname, avg(salary)
FROM aircraft, certified, employees
WHERE cruisingrange > 1000 AND ((certified.eid = employees.eid) = (aircraft.aid = certified.aid));
In one column, I am trying to display the aircraft names (aname) of all aircraft with cruising ranges over 1000 miles (cruisingrange > 1000). In the other column, I am trying to display the average salary (avg(salary)) of certified pilots (certified.eid = employees.eid) who are certified to fly that aircraft in particular (entire conditional after AND). However, it's conglomerating all the salaries into a single value and returning that, therefore I am only given a table with two columns and one row instead many rows (it only displays one aircraft name as well).
These SQL queries separately work just fine, though:
SELECT aname
FROM aircraft
WHERE cruisingrange > 1000;
SELECT avg(salary)
FROM employees, certified
WHERE employees.eid = certified.eid;
SELECT DISTINCT aname
FROM aircraft, certified
WHERE certified.aid = aircraft.aid;
How do I write a query that does what I'm supposed to do? I just started self-teaching SQL today so sorry if the answer is pretty obvious. Any suggestions are appreciated.
You are using MySQL, so use GROUP BY correctly:
SELECT aname, avg(salary)
FROM aircraft a JOIN
certified c
ON a.aid = c.aid JOIN
employees e
ON c.eid = e.eid
WHERE cruisingrange > 1000
GROUP BY aname;
Based on your query, you don't seem to understand SQL very well. So let's start there. Here is some advice:
Never use commas in the FROM clause. Always use proper, explicit JOIN syntax.
Use meaningful table aliases (abbreviations).
Qualify all column names (I can't in the above query, because I don't know where the columns come from).
Don't use DISTINCT until you understand SQL. It has big effects on performance and does strange unexpected things, sometimes.
So, what is happening with your query? Basically, consider this related query:
SELECT aname, avg(salary)
FROM aircraft a JOIN
certified c
ON a.aid = c.aid JOIN
employees e
ON c.eid = e.eid
WHERE cruisingrange > 1000 ;
How many rows does it return? Well . . . in most databases, it would return 0 rows and an error. This is an aggregation query, but the SQL engine does know what to do with aname.
MySQL allows this syntax, returning one row (because it is an aggregation query with no GROUP BY). The value of aname is taken from an indeterminate row.
Hence, when you add DISTINCT to the SELECT, it has no effect. The query already returns only one row, so it is, by definition, distinct.
I'm learning mySQL and I'm a little confused how I can use mySQL queries to say...list the total number of Employees in a Department.
Emp(eid: integer, ename: string, age: integer, salary: real)
Works(eid: integer, did: integer, pct_time: integer)
Dept(did: integer, budget: real, managerid: integer)
*this is the example my book is working with but I guess I'm just asking can someone help me walk through an example using the sum aggregate function? Or would I be totally off in using this to say...list the total number of employees?
You need to join the table Dept with Works first using LEFT JOIN in order for all records (all department) to be shown on the final result, those departments that have no employee will be on the list but with value of 0.
SELECT a.Did, COUNT(b.DId) TotalEmployee
FROM Dept a
LEFT JOIN Works b
ON a.Did = b.DId
GROUP BY a.Did
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
You're going to want to use SUM, like you said. So, you would first need to Join the tables, and then take a SUM of the employee_id row.
Select Emp.eid, SUM(Emp.eid) as employee_sum From, Dept.did
Emp
LEFT JOIN Works On
Emp.eid = Works.eid
INNER JOIN Dept ON
Works.did = Dept.did
Group By Emp.eid, Dept.did
The simplest way to do it is to simply using joins:
Select count(E.eid)
from Emp E,Works W,Dept D
where (E.eid = W.eid AND D.did = W.did);
Works table would be linking the two tables together ie Emp and Dept.
So this query will return you only the no of Employees in a department.