JOIN or Conditional Logic Between 4+ Tables - sql

Full disclosure, this is a homework question, but I have 20 more of these that are much more difficult, I just really can't wrap my head around some of the more complicated statements, so I'd appreciate the help before I just give up on the assignment completely. The professor and TA declined any help.
I'm using the 'HR' table that is present as a demo in all Oracle DBs. I need to create a unique listing of all jobs in a particular department along with the locations. It has the tables: REGIONS, LOCATIONS, DEPARTMENTS, JOBS, and EMPLOYEES of the form described below:
Locations:
Name Null? Type
----------------------------------------- -------- ----------------------------
LOCATION_ID NOT NULL NUMBER(4)
STREET_ADDRESS VARCHAR2(40)
POSTAL_CODE VARCHAR2(12)
CITY NOT NULL VARCHAR2(30)
STATE_PROVINCE VARCHAR2(25)
COUNTRY_ID CHAR(2)
Jobs:
Name Null? Type
----------------------------------------- -------- ----------------------------
JOB_ID NOT NULL VARCHAR2(10)
JOB_TITLE NOT NULL VARCHAR2(35)
MIN_SALARY NUMBER(6)
MAX_SALARY NUMBER(6)
Employees:
Name Null? Type
----------------------------------------- -------- ----------------------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
COMMISSION_PCT NUMBER(2,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
Departments:
Name Null? Type
----------------------------------------- -------- ----------------------------
DEPARTMENT_ID NOT NULL NUMBER(4)
DEPARTMENT_NAME NOT NULL VARCHAR2(30)
MANAGER_ID NUMBER(6)
LOCATION_ID NUMBER(4)
So of course I can think through how the relational algebra kind of goes, but I have no clue how it would go in SQL syntax. I of course know how to do more trivial joins, but I'm not understanding how to agregate all of the requirements into one statement. I want to get all UNIQUE or DISTINCT JOB_IDs where DEPARTMENT_ID=80,Then do a join on Jobs to get the matching JOB_TITLE,then derive LOCATION_ID from DEPARTMENTS.DEPARTMENT_ID and finally join Locations to my original JOB_TITLE selection WHERE LOCATION.LOCATION_ID=DEPARTMENTS.DEPARTMENT_ID. So what does that look like. Do I have to do 3 joins to get all the information in one selection?

This might help you. Add 'Where' condition as per your requirement.
select (select j.job_title from jobs j where j.JOB_ID=x.JOB_ID) as Job_Title ,
(select dp.department_name from department dp where dp.department_id) as Department_Name,
(select l.City || l.State_province from Location l where l.location_id = x.location_id) as Location
(select
e. JOB_ID,
D.DEPARTMENT_ID,
d.location_id
from Employees e inner join Department d on e.DEPARTMENT_ID = d.DEPARTMENT_ID
group by l.location_id,d.DEPARTMENT_ID , e.JOB_ID) x

This is intended to get you started. The core of your need comes from 2 tables employee (for job_id) and department (for location_id) and the relationship between employee and department is via the shared department_id column (this becomes the join)
SELECT
e.JOB_ID
, d.LOCATION_ID
FROM Employees e
INNER JOIN Departments d ON e.DEPARTMENT_ID = d.DEPARTMENT_ID
WHERE e.DEPARTMENT_ID = 80
Now you need to figure how to get the "uniqueness"
One way is to add select distinct and while this will work, it does limit your options
SELECT DISTINCT
e.JOB_ID
, d.LOCATION_ID
FROM Employees e
INNER JOIN Departments d ON e.DEPARTMENT_ID = d.DEPARTMENT_ID
WHERE e.DEPARTMENT_ID = 80
OR, to use GROUP BY which open up other possibilities such as aggregations
SELECT
e.JOB_ID
, d.LOCATION_ID
, COUNT(*) as count_of_employees
FROM Employees e
INNER JOIN Departments d ON e.DEPARTMENT_ID = d.DEPARTMENT_ID
WHERE e.DEPARTMENT_ID = 80
GROUP BY
e.JOB_ID
, d.LOCATION_ID
What I leave for you to discover is how to bring the names into the result.

Smth like that, perhaps
New-fashioned joins:
select tloc.*, tdept.*, tjobs.*
from locations tloc
inner join departments tdept on (tloc.location_id=tdept.location_id)
inner join employees templ on (tdept.department_id=templ.department_id)
inner join jobs tjobs on (templ.employee_id=tjobs.employee_id)
where
tdept.department_id=80
Old-fashioned joins:
select tloc.*, tdept.*, tjobs.*
from locations tloc, departments tdept, employees templ, jobs tjobs
where
tloc.location_id=tdept.location_id
and tdept.department_id=templ.department_id
and templ.employee_id=tjobs.employee_id
----
and tdept.department_id=80

Related

How to join multiple tables to get names of employees who earn more than their manager?

I have created two tables:
create table DEPARTMENTS
(
dept_id number,
dept varchar2(50) not null,
mgr_id number,
location varchar2(50),
constraint pk_departments primary key (dept_id)
);
create table EMPLOYEES
(
emp_id number,
fname varchar2(50) not null,
lname varchar2(50) not null,
email varchar2(50),
phone number,
hired date,
job_id varchar(20),
salary number(7,2),
commission_pct number(4,2),
dept_id number,
grade number,
constraint pk_employees primary key (emp_id),
constraint fk_employees_dept_id foreign key (dept_id)
references DEPARTMENTS (dept_id)
constraint fk_employees_grade foreign key (grade)
references SALGRADE (grade)
);
How go I get names of employees who earn more than their managers given that the mgr_id is in table departments and the employee is in employees table and they are linked by dept_id ?
I found that 'self-join' is needed. But this case is a bit difficult since referencing dept_id is needed to refer to the mgr_id then compare it to the emp_id and create join ?
For better clarity and self understanding, using ANSI joins:
SELECT * FROM
(SELECT E.SALARY AS MANAGER_SALARY, D.DEPT_ID AS DEPT_ID
FROM EMPLOYEES E
INNER JOIN
DEPARTMENTS D
ON E.DEPT_ID = D.DEPT_ID)
MGR_DET
INNER JOIN
EMPLOYEES EMP
ON MGR_DET.DEPT_ID = EMP.DEPT_ID
where EMP.SALARY > MGR_DET.MANAGER_SALARY;
You can do this with three joins. Start with the employees table, match to departments to get the manager. And then match back to employees using the manager.
This looks like:
select e.*, em.salary -- or whatever columns you want
from employees e join
departments d
on d.dept_id = e.dept_id join
employees em
on em.emp_id = d.mgr_id
where e.salary > em.salary
Answer with implicit joins:
SELECT E1.* FROM EMPLOYEES E1, DEPARTMENT D, EMPLOYEES E2
WHERE E1.DEPT_ID = D.DEPT_ID AND D.DEPT_ID = E2.DEPT_ID AND D.MGR_ID = E2.MGR_ID
AND E1.SALARY >= E2.SALARY;
Here the employee E1 is the employee whose salary is greater than that of his department manager.
To find that, we join the employee table E1 and department table D to get on dept_id and then fetch the mgr_id. Now the next step is to find the employee with the same ID as the mgr_id of the department. Hence we again join it with another employee table E2 on emp_id. Finally we compare the salary of employee e1 and the manager (employee e2).
Answer with explicit joins:
SELECT E1.* FROM EMPLOYEES E1 JOIN DEPARTMENT D ON E1.DEPT_ID = D.DEPT_ID
JOIN EMPLOYEES E2 ON (D.DEPT_ID = E2.DEPT_ID AND D.MGR_ID = E2.MGR_ID)
WHERE E1.SALARY >= E2.SALARY;

SQLite Multiple Subqueries Logic

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'

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'

Oracle SQL querying for average across several tables

Here are the relations:
CREATE TABLE employee (
name varchar2(15) not null,
ssn char(9),
sex char,
salary number(10,2),
dno number(4),
primary key (ssn),
foreign key (dno) references department(dnumber)
);
CREATE TABLE department (
dname varchar2(15) not null,
dnumber number(4),
primary key (dnumber),
unique (dname),
);
Q1: For each department whose average employee salary is more than $30,000, retrieve the department name and the number of employees working for that department.
Q2: Suppose that we want the number of male employees in each department rather than all employees (as in Q1) to calculate the departmental averages and number of employees. Can we specify this query in SQL? Why or why not.
Thanks!
Your first query might look like
SELECT d.dname,
COUNT(*) total_employees
FROM department d LEFT JOIN
employee e ON d.dnumber = e.dno
GROUP BY d.dname
HAVING AVG(e.salary) > 30000;
And the second one
SELECT d.dname,
COUNT(*) male_employees
FROM department d LEFT JOIN
employee e ON d.dnumber = e.dno
WHERE e.sex = 'M'
GROUP BY d.dname
HAVING AVG(e.salary) > 30000;
SQLFiddle
If you want to get the number of all employees and the number of male employees in one SQL,you can try this.
SELECT B.DNAME,
COUNT(*) AS TOTAL_EMPLOYEES,
COUNT(CASE WHEN A.SEX='M' THEN A.SEX END) AS MALE_EMPLOYEES,
AVG(A.SAL) AS ALL_AVG,
AVG(CASE WHEN A.SEX='M' THEN A.SAL END) AS MALE_AVG
FROM EMPLOYEE A JOIN DEPARTMENT B
ON A.DNO=B.DNUMBER
GROUP BY B.DNAME
SELECT dname, COUNT(*) AS Num_of_Employees
FROM employee, department
WHERE dno = dnumber AND EXISTS (
SELECT AVG(Salary)
FROM employee
GROUP BY dno
HAVING AVG(Salary)>30000)
GROUP BY dname;