SQL to join four tables - sql

Display a list of employees names,salaries,and the name of the city in which they work for IT developers who do not earn a comission who work in the operations department in Italy
I did this and it did not work
SELECT e.first_name, e.last_name, e.salary, l.city
FROM employees e
JOIN jobs j
ON (j.job_id = e.job_id)
WHERE
AND e.commission_pct = 0
AND job_title LIKE '%IT Developer%'
JOIN locations l
ON (l.location_id = d.location_id)
AND
(
l.country LIKE '%Italy%'
AND
department_name = 'Operations'
);
http://imgur.com/lex2SyV and this is the link for my logical ERD

SELECT e.first_name,e.last_name,e.salary,l.city
FROM employees e join jobs j
ON(j.job_id = e.job_id)
join departments d ON (d.department_id=e.department_id)
join locations l
ON (l.location_id = d.location_id)
WHERE e.commission_pct = 0 AND
job_title LIKE '%IT Developer%'
AND (l.country LIKE '%Italy%'
AND d.department_name = 'Operations')

Related

Complex query - retrieve employees working in both two specific departments

I'm trying to figure a way to retrieve employees working in two different departments.
I have 3 simple tables:
employee (employee_id, employee_name)
department (department_id, department_name)
working (eid, did, work_time)
So I have tried to write a SQL query:
select employee_name
from employee, working,department
where eid = employee_id
and did = department_id
and department_name = 'software'
and dname = 'hardware';
But it doesn't work, what is my problem?
The problem is that you are requiring department to be both 'software' and 'hardware'. Also, dname is not a field.
Correcting your query:
select employee_name
from employee, working, department
where eid = employee_id and did = department_id
and (department_name = 'software' or department_name = 'hardware');
But I would prefer this kind of query:
SELECT DISTINCT e.employee_name
FROM employee e
JOIN working w ON w.eid = e.employee_id
JOIN department d ON d.department_id = w.did
WHERE d.department_name IN ('software', 'hardware');
That is to get employees that work in any of the two departments (or both).
If you want only employees that work in both departments, try this:
SELECT e.employee_id, e.employee_name
FROM employee e
JOIN working w ON w.eid = e.employee_id
JOIN department d ON d.department_id = w.did
WHERE d.department_name IN ('software', 'hardware')
GROUP BY e.employee_id HAVING COUNT(DISTINCT d.department_id) = 2;
Would something like this work for you?
SELECT
count(*) as cnt,
employee.employee_name
FROM
employee
JOIN working ON working.eid = employee.employee_id
JOIN department ON department.department_id = working.did
WHERE
department.department_name = 'software' or department.department_name = 'hardware'
GROUP BY employee.employee_name
HAVING cnt > 1
This would count each employee who is linked both software or hardware department. Or you can leave WHERE clause away to get all employees working more than one departments.
What is my problem?
There is no dname column in your tables.
You can simplify the problem as you don't need the department table since the working table contains the department id in the did column.
Then you need to GROUP BY each employee and find those HAVING a COUNT of two DISTINCT department ids:
SELECT MAX(e.employee_name)
FROM employee e
INNER JOIN working w
ON e.employee_id = w.eid
GROUP BY e.employee_id
HAVING COUNT(DISTINCT w.did) = 2
If you want to consider only the software and hardware departments then:
SELECT MAX(e.employee_name)
FROM employee e
INNER JOIN working w
ON e.employee_id = w.eid
INNER JOIN department d
ON w.did = d.department_id
WHERE d.department_name IN ('software', 'hardware')
GROUP BY e.employee_id
HAVING COUNT(DISTINCT w.did) = 2
You can easily obtain employees who work in one specific department:
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = 'software'
Now ambiguity cames. If you want to get all employees work either in software or in hardware:
-- Employees who work at either software or hardware or both departments
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = in ('software', 'hardware')
If you want to get employees who works in both software and hardware departments:
-- Employees who work in both hardware and software deparments simultaneously
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = 'software'
intersect
select *
from Employee e inner join
Working w on e.employee_id = w.eid inner join
Department d on w.did = d.department_id
where d.name = 'hardware'

How to check if a value is larger than the average of these values?

I need to find the a list of people who earn more money than the average salary in their country
SELECT e1.first_name || ' ' ||e1.last_name
FROM hr.employees e1
WHERE salary >= (
Select AVG(salary)
FROM HR.employees e2
INNER JOIN HR.departments ON e2.department_id = departments.department_id
INNER JOIN HR.locations ON HR.departments.location_id = HR.locations.location_id
INNER JOIN HR.countries ON HR.locations.country_id = hr.countries.country_id
GROUP BY hr.countries.country_id
);
The subquery gives me the average salary values per country and works right.
However when I try to combine them I get the following error:
ORA-01427: Subquery returns more than one row
Any idea what I should do to get the query working?
Use AVG as an analytic function:
SELECT full_name
FROM (
SELECT e.first_name || ' ' ||e.last_name AS full_name,
e.salary,
AVG( e.salary ) OVER ( PARTITION BY l.country_id ) AS avg_salary
FROM hr.employees e
INNER JOIN HR.departments d
ON e.department_id = d.department_id
INNER JOIN HR.locations l
ON d.location_id = l.location_id
)
WHERE salary > avg_salary;
Use window functions:
SELECT e.first_name || ' ' || e.last_name
FROM (SELECT e.*,
AVG(salary) OVER (PARTITION BY l.country_id) as country_avg
FROM HR.employees e JOIN
HR.departments d
ON e.department_id = d.department_id JOIN
HR.locations l
ON d.location_id = l.location_id
) e
WHERE e.salary >= e.country_avg;
Note that the country table is not needed. The country_id is sufficient to identify the country.

How can I write this query correctly?

How can I write a query that gives the country name, city, postal code, street address and the number of departments where at least 2 employees work? Below is the query I wrote, but I get "not a GROUP BY expression" error as a result of the query.
SELECT k.COUNTRY_NAME,
l.CITY,
l.POSTAL_CODE,
l.STREET_ADDRESS,
e.DEPARTMENT_ID,
COUNT(EMPLOYEE_ID)
FROM hr.employees e
JOIN hr.departments c
ON (c.DEPARTMENT_ID = e.DEPARTMENT_ID)
JOIN hr.locations l
ON (c.LOCATION_ID = l.LOCATION_ID)
JOIN hr.countries k
ON (k.COUNTRY_ID = l.COUNTRY_ID)
GROUP BY e.DEPARTMENT_ID
HAVING COUNT(EMPLOYEE_ID) > 2;
Because all non-aggregated columns(c.country_name,l.city,l.postal_code,l.street_address,e.department_id) should be listed within the GROUP BY list which is not suitable for your case. Rather use COUNT(.) OVER (..) analytic function with PARTITION BY e.department_id option in order to group by department_id column such as
SELECT DISTINCT *
FROM
(
SELECT c.country_name,
l.city,
l.postal_code,
l.street_address,
e.department_id,
COUNT(e.employee_id) OVER (PARTITION BY e.department_id) AS count
FROM hr.employees e
JOIN hr.departments d
ON d.department_id = e.department_id
JOIN hr.locations l
ON d.location_id = l.location_id
JOIN hr.countries c
ON c.country_id = l.country_id
)
WHERE count >= 2 -- equality is added considering "at least" 2
ORDER BY count
Btw, the parentheses next to the ON clause are redundant
You would first get the set of all departments that have at least 2 employees as follows(atleast_two)
After that you would join the data with the rest of your query and pull the attributes of interest.
with atleast_two
as (select c.DEPARTMENT_ID
,count(employee_id) as cnt_employees
from hr.employees e
join hr.departments c
on (c.DEPARTMENT_ID=e.DEPARTMENT_ID)
group by c.deptid
having count(employee_id)>2
)
select k.COUNTRY_NAME
, l.CITY
, l.POSTAL_CODE
, l.STREET_ADDRESS
, e.DEPARTMENT_ID
, c.cnt_employees
from hr.employees e
join atleast_two c
on (c.DEPARTMENT_ID=e.DEPARTMENT_ID)
join hr.locations l
on (c.LOCATION_ID=l.LOCATION_ID)
join hr.countries k
on (k.COUNTRY_ID=l.COUNTRY_ID);

How to debug this SQL query?

I run this code and it does not work (I am expecting a result set but I do not get one):
select e.employee_id,e.first_name,e.last_name,e.salary,d.department_name,l.city from employees e
join departments d on e.department_id = d.department_id
join locations l on l.location_id = d.location_id
where e.salary = select max(salary) from employees where hire_date between '2002-01-01' and '2003-12-31';
however if I run the queries
select max(salary) from employees where hire_date between '2002-01-01' and '2003-12-31';
and
select e.employee_id,e.first_name,e.last_name,e.salary,d.department_name,l.city from employees e
join departments d on e.department_id = d.department_id
join locations l on l.location_id = d.location_id
where e.salary = 24000.00
they run fine. max(salary) from the second query is 24000.00.
This is the website where I am trying to practice (question no 34)
https://www.w3resource.com/sql-exercises/sql-subqueries-exercises.php
You are missing parenthesis for sub-query
SELECT e.employee_id,
e.first_name,
e.last_name,
e.salary,
d.department_name,
l.city
FROM employees e
JOIN departments d
ON e.department_id = d.department_id
JOIN locations l
ON l.location_id = d.location_id
WHERE e.salary = (SELECT Max(salary)
FROM employees
WHERE hire_date BETWEEN '2002-01-01' AND '2003-12-31');
Please use "IN" clause instead of "=" while filtering using a Subquery
SELECT e.employee_id,
e.first_name,
e.last_name,
e.salary,
d.department_name,
l.city
FROM employees e
JOIN departments d
ON e.department_id = d.department_id
JOIN locations l
ON l.location_id = d.location_id
WHERE e.salary IN (SELECT Max(salary)
FROM employees
WHERE hire_date BETWEEN '2002-01-01' AND '2003-12-31');

Difference between old and new SQL JOINS

I am currently learing SQL and I can't understand why these two queries return different numbers of rows (the first one returns 53, while the second returns 69).
SELECT d.department_name "dept_name",
j.job_title "job_title",
e.manager_id "manager_id",
MAX(e.salary) "max_salary",
SUM(e.salary) "sum_salary"
FROM employees e, jobs j, departments d
WHERE j.job_id = e.job_id AND
d.department_id(+) = e.department_id
GROUP BY GROUPING SETS( (d.department_name, j.job_title), (j.job_title,e.manager_id), ());
And the second one:
SELECT d.department_name AS "dept_name",
j.job_title AS "job_title",
e.manager_id AS "manager_id",
MAX(e.salary) AS "max_salary",
SUM(e.salary) AS "sum_salary"
FROM employees e
INNER JOIN jobs j ON j.job_id = e.job_id
RIGHT OUTER JOIN departments d ON d.department_id = e.department_id
GROUP BY GROUPING SETS( (d.department_name, j.job_title), (j.job_title,e.manager_id), ());
Thank you for your help!
Your queries differ because in the first one you outer join table departments, whereas in the second you outer join jobs and employees. (Which is why I don't like right outer joins so much. I don't find them very readable.) You take employees, join with jobs and then by RIGHT OUTER JOIN you say: and give me all departments anyhow, so give me additional (outer joined records) for jobs and employees. The second statement is equal to:
SELECT d.department_name AS "dept_name",
j.job_title AS "job_title",
e.manager_id AS "manager_id",
MAX(e.salary) AS "max_salary",
SUM(e.salary) AS "sum_salary"
FROM departments d
LEFT OUTER JOIN employees e ON e.department_id = d.department_id
LEFT OUTER JOIN jobs j ON j.job_id = e.job_id
GROUP BY GROUPING SETS( (d.department_name, j.job_title), (j.job_title,e.manager_id), ());
In old Oracle syntax you would write:
SELECT d.department_name "dept_name",
j.job_title "job_title",
e.manager_id "manager_id",
MAX(e.salary) "max_salary",
SUM(e.salary) "sum_salary"
FROM employees e, jobs j, departments d
WHERE e.department_id(+) = d.department_id
AND j.job_id(+) = e.job_id;
In old-style joins the syntax
FROM employees e, departments d
WHERE d.department_id(+) = e.department_id
is a left join and is equal to:
FROM employees e
LEFT JOIN departments d ON d.department_id = e.department_id
For these queries two work exactly the same, you should also change the RIGHT JOIN to LEFT JOIN in the second query, or d.department_id(+) = e.department_id to d.department_id = e.department_id(+) in the first.