Joining three tables (link table) - sql

Need help with a query that I wrote:
I have three tables
Company
id name
1 Gary's
Employee
id name company_id
1 Tim Jones 1
2 Sam Adams 1
reports to
employee_id reports_to_id
1 2
My current query is:
select
temp.company.name as comp_name,
temp.employee.name as employee_name,
temp.employee.id as employee_id
from temp.employee, temp.employee
where temp.company.id = temp.employee.company_id and temp.company.id = 1
Which gives me the output of:
comp_name employee_name employee_id
Gary's Tim Jones 1
I need something like this:
comp_name employee_name reports_to
Gary's Tim Jones Sam Adams
What's a good way to modify my query to do this? I have a query and then I take those results and run a second query against that result set (which is excessively unnecessary).

Assuming an employee only reports to one person then we could have (no link table)
Employee (Id, Name, CompanyId, ReportsToId)
Company (Id, Name)
Then you could have a query similar to
select e.Name EmployeeName, c.Name CompanyName, r.Name ReportsTo
from
Employee e
inner join Company c on e.CompanyId = c.Id
inner join Employee r on e.ReportsToId = r.Id
where
e.CompanyId = 1
If the employee reports to multiple people then we would use a link table
Employee (Id, Name, CompanyId)
EmployeeReportsTo (EmployeeId, ManagerId)
Company (Id, Name)
select e.Name EmployeeName, c.Name CompanyName, r.Name ReportsTo
from
Employee e
inner join Company c on e.CompanyId = c.Id
inner join EmployeeReportsTo ert on ert.EmployeeId = e.Id
inner join Employee r on ert.ManagerId = r.Id
where
e.CompanyId = 1

Related

Query to get employee name and manager name that are from different tables

Employee Table
EmployeeNumber (PK)
PersonID
ReportstoManagerEmployeeNumber (i.e.- the employee number of the employee's manager)
Names Table
PersonID (PK)
FirstName
LastName
I want to display FirstName, LastName, EmployeeNumber, ReportstoManagerEmployeeNumber , Firstname as managerfirstname, LastName as managerlastname
Basically I want to select the first name and last name of the ReportstoManagerEmployeeNumber column
What I have tried:
SELECT n.FirstName, n.LastName, emp.EmployeeNumber,
emp.ReportstoManagerEmployeeNumber, n.firstname as managerfirstname, n.lastname as managerlastname
FROM Names n
INNER JOIN employees emp
ON n.personID = emp.personID
INNER JOIN employees emp2
ON n.personID = emp2.personID
I was thinking a self-join but this won't work as this just selects the names of the employees from the first and second column. I am new to SQL but I believe a subquery or a CTE is required but I am not sure how to set it up.
To clarify-- John Smith has personID = 1 in the Names table but has employeeID = 2 in the employee table.
It is the other way around, you need to join 2 Names tables one for the employee and one for the manager
SELECT
emp.EmployeeNumber,
n.FirstName,
n.LastName,
emp.ReportstoManagerEmployeeNumber,
n1.firstname AS managerfirstname,
n1.lastname AS managerlastname
FROM
employees emp
INNER JOIN
Names n ON n.personID = emp.PersonID
INNER JOIN
employees emp2 ON em2.EmployeeNumber= emp.ReportstoManagerEmployeeNumber
INNEr JOIN
Names n1 ON n1.personID = emp2.personID

SQL count occurrences of an id from another table in multiple rows

TABLE 1 employee:
employee_id, first_name, last_name
2 John Appleseed
TABLE 2 performance_review:
employee_id, reviewer_id
2 1
2 3
2 4
1 2
3 2
QUESTION: print the first_name and last_name in a single row, then how many times that id is found in the employee_id column, then how many times that same id is found in the reviewer_id column.
Example output:
Name Employee_id count Received_review count
-------------------------------------------------------------
John Appleseed 3 2
What I got so far (it doesn't work)
SELECT
CONCAT([employee_first_name], ' ' , [employee_last_name]) AS employee_full_name,
(SELECT COUNT(employee.employee_id)
FROM performance_review AS received_review
LEFT JOIN performance_review ON employee.employee_id = performance_review.employee_id) AS received_reviews
FROM
employee
Since this involves separate aggregation over two different columns you need two subqueries, one for each.
Here is an example [edit] left joins should be used here because the inner joins would fail for example if the performance review table has all rows with null reviewer for a particular employee.
with
emp as (select employee_id,count(*) employee_count
from performance_review
group by employee_id),
rev as (select reviewer_id,count(*) reviewer_count
from performance_review
group by reviewer_id)
select
first_name,
last_name,
employee_count,
reviewer_count
from
employee
left join emp on employee.employee_id=emp.employee_id
left join rev on employee.employee_id=rev.reviewer_id;
The result
first_name
last_name
employee_count
reviewer_count
John
Appleseed
3
2
Robert's answer is the clearest way to do it but I thought I would show another way to do it with a join -- here you use a trick of doing a test and sum to count certain items. I join both cases
SELECT e.first_name, e.last_name,
SUM(CASE WHEN e.employee_id = p.employee_id THEN 1 ELSE 0 END) as employee_count,
SUM(CASE WHEN e.employee_id = p.reviewer_id THEN 1 ELSE 0 END) as reviewer_count
FROM employee e
LEFT JOIN performance_review p on e.employee_id = p.reviewer_id
or e.employee_id = p.employee_id
GROUP BY e.first_name, e.last_name

find all employees' names who has a manager that lives in the same city as them

I am trying to figure out how to "find all employees' name who has a manager that lives in the same city as them." For this problem, we have two tables. We need to make a query.
"employee"
The employee table that we can refer to has both normal employees and managers
employeeid
name
projectid
city
1
jeff
1
new york
2
larry
1
new york
3
Linda
2
detroit
4
tom
2
LA
"Managertable"
Our manager table which we can refer to with mangerid = employeeid
projectid
mangerid
1
2
2
3
Right now I have found a way to get just the employees and filter out the managers, but now I am trying to figure out the next step to get to the comparison of managers and employees. Would this just be another subquery?
SELECT name
FROM employee e
WHERE employeeid not in(
SELECT mangerid
FROM Managertable pm
INNER JOIN employee e
ON pm.mangerid= e.employeeid);
Expected result :
employee name
jeff
I think the easient way to achieve this would be like this:
SELECT
e.*
FROM employee e
inner join Managertable mt on e.projectid = mt.projectid
inner join employee manager on mt.mangerid = manager.employeeid
WHERE
e.city = manager.city
and e.employeeid <> manager.employeeid;
One approach is a correlated subquery in which we look up the employee's manager's city.
select e.name
from employee e
where city =
(
select m.city
from managertable mt
join employee m on m.employeeid = mt.managerid
where mt.projectid = e.projectid
and m.employeeid <> e.employeeid
);
The same thing can be written with an EXISTS clause, if you like that better.
Based off the table structure you're showing, something like this might work
First find the employee ids of employees who have managers in the same city, then join it back on employee to retrieve all data from the table
;WITH same_city AS (
SELECT DISTINCT e.employeeid
FROM employee AS e
INNER JOIN managertable AS mt ON e.projectid = mt.projectid
INNER JOIN employee AS m ON mt.managerid = e.employeeid
WHERE e.city = m.city
)
SELECT e.*
FROM employee
INNER JOIN same_city AS sc ON e.employeeid = sc.employeeid
I don't see how projectid is relevant in your question because you didn't mention that as a requirement or restriction. Here's a method using a CTE to get the managers and their cities, then join to it to find employees who live in the same city as a manager.
with all_managers as (
select distinct m.managerid, e.city
from manager m
join employee e
on m.managerid = e.employeeid
)
select e.name
from employee e
join all_managers a
on e.city = a.city
and e.employeeid <> a.managerid;
name
jeff
But it you want us to assume that an employee reports to only that manager as listed in the projectid, then here's a modification to ensure that is met:
with all_managers as (
select distinct m.managerid, e.city, e.projectid
from manager m
join employee e
on m.managerid = e.employeeid
)
select e.name
from employee e
join all_managers a
on e.city = a.city
and e.projectid = a.projectid
and e.employeeid <> a.managerid;
View on DB Fiddle
You just need two joins:
one between "managers" and "employees" to gather managers information
one between "managers" and "employees" to gather employees information with respect to the manager's projectid and city.
SELECT employees.name
FROM managers
INNER JOIN employees managers_info
ON managers.mangerid = managers_info.employeeid
INNER JOIN employees
ON managers.projectid = employees.projectid
AND managers_info.employeeid <> employees.employeeid
AND managers_info.city = employees.city

How to get column value from "nested relation" in sql

Not sure of the correct phrasing to use here, but here's the case.
I have 3 tables.
Employee
id
name
department_id
1
Jon Doe
2
Department
id
name
office_id
2
Accounting
3
Office
id
name
3
London
Now, getting the employee with department name is straight forward:
select employee.id, employe.name, department.name as department_name, department.office_id
from employee
inner join department on employee.department_id = department.id
How can i also include the office name in the result?
You can use a second inner join for the office:
select employee.id, employe.name, department.name as department_name, department.office_id, office.name as office_name
from employee
inner join department on employee.department_id = department.id
Inner join office on office.id = department.office_id

Include additional fields when using COUNT() with JOINed tables?

I'm going to learn SQL, and having troubes with one of the excercises. "Get the number of customers each employee is responsible for" (I hope the translation is OK). I figured out following:
SELECT emp.EmployeeId, COUNT(cus.SupportRepId) AS Customers
FROM Employee AS emp JOIN Customer AS cus
ON cus.SupportRepId = emp.EmployeeId
GROUP BY emp.EmployeeId
The result is correct so far:
EmployeeId Customers
-------------------------
3 21
4 20
... ...
Now I thought it would be nice to addional employee's data in the result too (e.g. JobTitle from the table 'Employee'), but this doesn't seems to work:
SELECT emp.EmployeeId, emp.JobTitle, COUNT(cus.SupportRepId) AS Customers
FROM Employee AS emp JOIN Customer AS cus
ON cus.SupportRepId = emp.EmployeeId
GROUP BY emp.EmployeeId
I don't understand why. What should I have to in order to get the expected result:
EmployeeId JobTitle Customers
------------------------------------------------
3 Key Account Manager 21
4 Business Area Manager 20
... ...
I hope you can help. Have much thanks in before.
You can throw it in the GROUP BY:
SELECT e.EmployeeId, e.JobTitle, COUNT(c.SupportRepId) AS Customers
FROM Employee e JOIN
Customer c
ON c.SupportRepId = e.EmployeeId
GROUP BY e.EmployeeId, e.JobTitle;
Alternatively, you can use a correlated subquery or aggregate before joining:
SELECT e.*, c.Customers
FROM Employee e JOIN
(SELECT c.SupportRepId, COUNT(c.SupportRepId) AS Customers
FROM Customer c
GROUP BY c.SupportRepId
) c
ON c.SupportRepId = e.EmployeeId;