SQLite Multiple Subqueries Logic - sql

The problem is asking for one to write a query to find the names (first_name, last_name) of the employees who have a manager who works for a department based in the United States. Here is a link to the problem, to see the tables, https://www.w3resource.com/sqlite-exercises/sqlite-subquery-exercise-3.php.
For the subquery, I did a left join on the location id between the department and location tables then I selected 'US' for the country_id, and returned the manager_id
For the outer query, I chose the Employee table, selected manager_ids from sub-query list.
SELECT first_name, last_name
FROM Employees
WHERE manager_id IN (SELECT manager_id
FROM Departments d LEFT JOIN Locations l ON d.location_id = l.location_id
WHERE country_id = 'US')
ORDER BY first_name;
With my code, I do not get the correct answer, the same results as the result-set/output shown on the website.
There are three subqueries in total in the correct answer. I do not understand what the purpose of including the subquery involving the employees table (outermost subquery). I understand that is where I messed up but don't understand why.
SELECT 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')));

You need to join all the tables:
select e.first_name, e.last_name
from employees e
inner join employees m on m.employee_id = e.manager_id
inner join departments d on d.department_id = m.department_id
inner join locations l on l.location_id = d.location_id
where l.country_id='US'

Related

SQL in Oracle HR Schema

I have made a query in Oracle HR schema to see the following information:
The city where the department is located
The total number of employees in the department
However, the query cannot be executed correctly and said this is "not a GROUP BY expression".
Does anyone knows what's the problem is? Thanks in advance.
SELECT department_name, city, COUNT(employees.department_id)
FROM departments
JOIN employees on (departments.department_id=employees.department_id)
JOIN locations USING (location_id)
GROUP BY department_name;
You are grouping by department and want to show the department's city. You expect this to work, because each department is in exactly one city. (SQL people call this functional dependency.)
For this to work, ...
there would have to be a unique contraint on the department name or you'd have to group by department_id instead
the DBMS must detect and support functional dependency in aggregation queries
Unfortunately, Oracle doesn't support functional dependency in aggregation queries. It forces us to put every such column in the GROUP BY clause or into an aggregation function.
So either extend the GROUP BY clause:
SELECT d.department_name, l.city, COUNT(e.department_id)
FROM departments d
JOIN employees e ON e.department_id = d.department_id
JOIN locations l USING (location_id)
GROUP BY d.department_name, l.city
ORDER BY d.department_name;
or use some aggregation function as MIN or MAX on that single value.
SELECT d.department_name, MAX(l.city) AS city, COUNT(e.department_id)
FROM departments d
JOIN employees e ON e.department_id = d.department_id
JOIN locations l USING (location_id)
GROUP BY d.department_name
ORDER BY d.department_name;
What I prefer though, is to aggregate first and only then join. You want to join the departments with their employee count, so do just that:
SELECT d.department_name, l.city, COALESCE(e.cnt, 0) AS employee_count
FROM departments d
JOIN locations l USING (location_id)
LEFT JOIN
(
SELECT department_id, COUNT(*) as cnt
FROM employees
GROUP BY department_id
) e ON e.department_id = d.department_id
ORDER BY d.department_name;
The problem is you have both aggregated and non-aggregated column (in your case city in the select list.
As I don't know the structure of location table and considering a department have only one location defined you can use max(city),
SELECT department_name, max(city) city, COUNT(employees.department_id) no_of_employees
FROM departments
JOIN employees on (departments.department_id=employees.department_id)
JOIN locations USING (location_id)
GROUP BY department_name;
As excellently explained by Thorsten, you could also group the data using OVER and PARTITION BY function which would eliminate the use of GROUP BY function.
SELECT d.department_name, l.city, COUNT(e.department_id) OVER (PARTITION BY e.department_id) as emp_count
FROM departments d
JOIN employees e ON e.department_id = d.department_id
JOIN locations l USING (location_id)
ORDER BY d.department_name;

Subquery using 3 Tables SQL

I'm trying to display the last name of the lowest paid employees from each city. The city column falls under a table titled LOCATIONS while employee information(salary, last name) falls under EMPLOYEES. Both of these tables are related share no common table, so I have to rely on a third table, DEPARTMENTS to connect the two as DEPARTMENTS contains a department_id that it shares with EMPLOYEES as well as a LOCATION_ID that it shares with LOCATIONS. This is what I have so far, but I'm having trouble with this as I've mostly worked with only two tables in the past.
SELECT LAST_NAME
FROM EMPLOYEES
WHERE (DEPARTMENT_ID) IN
(SELECT DEPARTMENT_ID
FROM DEPARTMENTS
WHERE LOCATION_ID IN
(SELECT LOCATION_ID
FROM LOCATIONS
GROUP BY CITY
HAVING MIN(SALARY)));
This seems to be an assignment in an intro course in SQL. So let's assume you can't use analytic functions, match_recognize clause, etc. Just joins and aggregates.
In the subquery in the WHERE clause below, we compute the min salary for each city. We need to join all three tables for this. Then in the overall query we join the three tables again, and we use the subquery for an IN condition (a semi-join). The overall query looks like this:
select e.last_name
from employees e join departments d
on e.department_id = d.department_id
join locations l
on d.location_id = l.location_id
where ( e.salary, l.city ) in
(
select min(salary), city
from employees e join departments d
on e.department_id = d.department_id
join locations l
on d.location_id = l.location_id
group by city
)
;
You should separate out the concept of table joins from WHERE clauses.
Use WHERE for filtering data, use JOIN for connecting data together.
I think this is what you are wanting. By the way, lose the ALL CAPS if you can.
SELECT
LAST_NAME
FROM
EMPLOYEES
INNER JOIN (
SELECT
DEPARTMENTS.DEPARTMENT_ID,
CITY,
MIN(SALARY) AS LOWEST_SALARY
FROM
EMPLOYEES
INNER JOIN DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
INNER JOIN LOCATIONS ON DEPARTMENTS.LOCATION_ID = LOCATIONS.LOCATION_ID
GROUP BY
DEPARTMENTS.DEPARTMENT_ID,
LOCATIONS.CITY
) AS MINIMUM_SALARIES
ON EMPLOYEES.DEPARTMENT_ID = MINIMUM_SALARIES.DEPARTMENT_ID
AND EMPLOYEES.SALARY = MINIMUM_SALARIES.LOWEST_SALARY
First of all join the tables, so you see city and employee in one row. If we group by city we get the minimum salary per city.
with city_employees as
(
select l.city, e.*
from locations l
join departments d using (location_id)
join employees e using (department_id)
)
select last_name
from city_employees
where (city, salary) in
(
select city, min(salary)
from city_employees
group by l.city
);
It is easier to achieve the same, however, with window functions (min over or rank over here).
select last_name
from
(
select
e.last_name,
e.salary,
min(e.salary) over (partition by l.city) as min_salary
from locations l
join departments d using (location_id)
join employees e using (department_id)
)
where salary = min_salary;

Sql query from multiple tables - joins not working

I have tried sub queries and join queries, but I cannot get the right answer, as I need to retrieve data from different tables. This is what I am trying to do:
Show the department Name, Street Address and
City of the department location and the Country Name
where the department is located for an employee
with the name Den Raphaely
I have tables like this.
COUNTRIES TABLE
country_id
country_name
region_id
DEPARTMENT TABLE
department_id
department_name
manager_id
location_id
EMPLOYEES TABLE
first_name
last_name
email
phone
salary
commission
department_id
job_id
hire_date
JOBS_HISTORY TABLE
employee_id
start_date
end_date
job_id
department_id
JOB TABLE
Job_id
Min_salary
Max_Salary
LOCATIONS TABLE
Location_id
Street_address
Postal_code
State
Country_id
REGION TABLE
region_id
region_name
This is the attempted query
SELECT Employees.FIRST_NAME, Employees.LAST_NAME,
Departments.DEPARTMENT_NAME, Locations.Street_Address,
Locations.City, Countries.Country_Name FROM
Countries INNER JOIN Locations ON
Countries.Country_ID=Locations.Country_ID
INNER JOIN Departments ON
Locations.Location_id=Departments.LOCATION_ID
INNER JOIN Employees ON
Departments.DEPARTMENT_ID = (SELECT Employees.DEPARTMENT_ID FROM Employees
WHERE FIRST_NAME LIKE 'Den' AND LAST_NAME LIKE 'Raphaely')
You are confusing your JOINs with your filtering (WHERE) logic:
SELECT
Employees.FIRST_NAME,
Employees.LAST_NAME,
Departments.DEPARTMENT_NAME,
Locations.Street_Address,
Locations.City,
Countries.Country_Name
FROM Countries
INNER JOIN Locations ON Countries.Country_ID=Locations.Country_ID
INNER JOIN Departments ON Locations.Location_id=Departments.LOCATION_ID
INNER JOIN Employees ON Departments.DEPARTMENT_ID = Employees.DEPARTMENT_ID
WHERE Employees.FIRST_NAME LIKE 'Den' AND Employees.LAST_NAME LIKE 'Raphaely'
SELECT e.first_name, e.last_name, d.department_name, l.street_address, l.city, c.country_name
FROM
employees e
INNER JOIN
department d
ON e.department_id = d.department_id
INNER JOIN
location l
ON d.location_id = l.location_id
INNER JOIN country c
ON c.country_id = l.country_id
WHERE
e.first_name = 'Den'
AND
e.last_name = 'Raphaely'

SQL query errors and mistakes

I have an issue with a SQL query.
The question: show all the departments in which the max salary is bigger than 10000.
I am getting an output with this but it doesn't seem right.
My code:
SELECT
Department_Name, Max_Salary
FROM
Departments
INNER JOIN
Job_History ON Departments.department_id = Job_History.department_id
INNER JOIN
Jobs ON Job_History.job_id = jobs.job_id
WHERE
Max_Salary > 10000
Output:
DEPT_NAME | MAX_SALARY
------------------------
Accounting | 16,000
Sales | 12,080
Sales | 20,080
There is only one Sales department in the database.
Any help on why this is happening would be appreciated.
Likely, there are multiple rows in job_history that are related to 'Sales' Department.
The join operation is returning all matching rows.
To get a distinct list of Department_Name, you could add GROUP BY Department_name to the end of the query. You'll also want to use an aggregate function around the Max_Salary column in the select list... e.g. MAX(Max_Salary).
Best practice is to qualify all column references in the query. For a reader not familiar with the database schema, it's not clear whether Max_Salary is from the Job_History table, or the Job table. Also, the keyword INNER has no effect on the join operation, that keyword can be omitted.
--This works
SELECT d.department_name
, MAX(j.max_salary) AS max_salary
FROM Departments d
JOIN Job_History h
ON h.department_id = d.department_id
JOIN Jobs j
ON j.job_id = h.job_id
WHERE j.max_salary > 10000
GROUP BY d.department_name
I would prefer to comment, but I don't have a high enough reputation.
Can you try this and see what it brings for the ids.
Select Department_Name, Max_Salary,D.department_id,J.job_id
From Departments D
INNER JOIN Job_History J_H
ON D.department_id=J_H.department_id
INNER JOIN Jobs J
ON J_H.job_id=J.job_id
WHERE Max_Salary > 10000
Select d.department_name
, MAX(j.max_salary) AS max_salary
From Departments D
INNER JOIN Job_History J_H
ON D.department_id=J_H.department_id
INNER JOIN Jobs J
ON J_H.job_id=J.job_id
GROUP BY d.department_name
having max(j.max_salary)>10000

Include rows that contain null values in a particular column with inner join

The goal here is to return the ID, Name, and Manager Name and ID for each Employee. The table does not contain a manager name, only a manager ID for a given employee. The current query works great using an inner join, except that one employee does not have a manager ID (he's the boss), and so none of his information appears. The current query looks like this:
SELECT DISTINCT e.employee_id AS EMPLOYEE_ID,
e.FULL_NAME AS EMPLOYEE_NAME,
m.manager_ID AS REPORTS_TO,
m.FULL_NAME AS MANAGER_NAME
FROM EMPS e
INNER JOIN EMPS m ON e.manager_id = m.employee_id;
How can I include the name and information for this employee despite his lack of a seemingly necessary field? Thanks
If you want to include the employee name when there is no manager do a left join:
SELECT DISTINCT e.employee_id AS EMPLOYEE_ID,
e.FULL_NAME AS EMPLOYEE_NAME,
m.manager_ID AS REPORTS_TO,
m.FULL_NAME AS MANAGER_NAME
FROM EMPS e
LEFT JOIN EMPS m ON e.manager_id = m.employee_id;