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

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;

Related

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

Calculate SUM from one table and display columns from another

I have 2 tables, Employee, and Transaction
Transaction
tID
cID
carID
eID
tDate
PickupDate
ReturnDate
Amount_Due
Employee
eID
fName
lName
Job
Manager
Hired
I need to calculate the commission (2.5%) and display that along with fName and lName.
I've calculated the commission, I think I've done the join correctly but can't quite figure out how to add in a SELECT to show the other two columns.
SELECT t.Amount_Due
, SUM(t.Amount_Due*0.025) AS Commission
FROM [Transaction] t
JOIN Employee e ON t.eID = e.eID
GROUP BY t.Amount_Due
You are grouping by the wrong columns, and you are trying to select Amount_Due and aggregate it at the same time:
SELECT e.fName
, e.lName,
, SUM(t.Amount_Due * 0.025) AS Commission
FROM [Transaction] t
JOIN Employee e
ON t.eID = e.eID
GROUP BY e.ID, e.fName, e.lName;
Probably just a typo - the JOIN should probably be ON t.eID = e.eId. You need to include both tables (table aliases) involved - you're currently just using the t.eID twice.
Try this :
SELECT
t.Amount_Due, SUM(t.Amount_Due*0.025) AS Commission
FROM
[Transaction] t
JOIN
Employee e ON t.eID = e.eID
GROUP BY
t.Amount_Due
As pointed out by #charlieface - it seems very odd that you're summing the Amount_due, and at the same time trying to group by that same column..... my best guess is that you probably want to group by the employee, based on their eID - so use something like this:
SELECT
e.eID, e.fName, e.lName, SUM(t.Amount_Due*0.025) AS Commission
FROM
[Transaction] t
JOIN
Employee e ON t.eID = e.eID
GROUP BY
e.eID, e.fName, e.lName
for that.

How to get all departments with Employee number

I have an EmployeeDepartmetn juction table like this. I have all the departments in Depeartment table and employees in Employee table..
I want to get departments for an particular employee along with the all the departments available in depeartment table.
It should be like Select DepartmentId, DepartmentName, EmployeeID from Query.
Main criteria here is, Need to display NULL if the employee dont have that department. I am confused here...please help.
Please give Linq Query
Thanks in Advance
Put criteria in your left join:
Select distinct a.DeptID, b.DepartmentName, b.EmployeeID
From Department a
left join EmployeeDepartment b
on a.DeptID = b.DeptID and b.EmployeeID = 1 --insert employee ID here
It will show all departments (even those with no employees), then show the employee ID you chose in the third column only if that employee is assigned there.
You can do this with conditional aggregation:
select DeptId,
max(case when EmployeeId = 1 then EmployeeId end) as EmployeeId
from EmployeeDepartment ed
group by DeptId;
EDIT:
If you have a departments table as well:
select d.deptid, d.name, ed.employeeid
from Departments d left join
EmployeeDepartment ed
on d.deptid = ed.deptid and
ed.employeeid = 1;

Trying to tie the information from three different tables into one with a join query

So I have three queries I'm trying to join all on the department name field, but I don't know how to do a join on them. Each of my queries below give me some or part of the info I'm looking, but I want the information combined in one table, by department.
the first table is: (total employee count by department)
`
select d.name, count(*)
from employee e join department d
on e.dept_id = d.dept_id
group by d.name;
`
which gives me this:
Administration 3
Loans 1
Operations 14
the second is:
`
select d.name, m.emp_id
from employee e join employee m
on e.superior_emp_id = m.emp_id
join department d
on d.dept_id = e.dept_id
group by d.name, m.emp_id;
`
which gives me this (the manager id number, and which dept they work in)
Operations 3
Loans 4
Operations 6
Operations 10
Operations 13
Operations 4
Operations 16
Administration 1
and my third table:
`
select d.name, b.city
from employee e join branch b
on e.assigned_branch_id = b.branch_id
join department d
on d.dept_id = e.dept_id
group by d.name, b.city;
`
which gives me this:
Administration Waltham
Operations Waltham
Operations Woburn
Loans Waltham
Operations Quincy
Operations Salem
I want a table that just gives me the sum totals by the dept (admin, loans, operations) for the employees (the first table), the managers (total managers managing employees for each dept) and how many different branch locations each dept is located at. So the query should come back with
Dept| Staff Count | Location Count | Manager Count with their respective totals by each department.
I'm trying to figure out how to do the join query on this. Any help would be greatly appreciated.
You should be able to get the results you are looking for by using subselects
SELECT
d.Name AS Dept
,(SELECT COUNT(*)
FROM Employee e
WHERE e.dept_id = d.dept_id ) AS StaffCount
,(SELECT COUNT(*)
FROM Employee e
JOIN Branch b ON e.assigned_branch_id = b.branch_id
WHERE e.dept_id = d.dept_id GROUP BY b.city) AS LocationCount
,(SELECT COUNT(*)
FROM Employee e
JOIN Employee m ON e.superior_emp_id = m.emp_id
WHERE e.dept_id = d.dept_id GROUP BY m.emp_id) AS ManagerCount
FROM department d

SQL Server Query using GROUP BY

I am having trouble writing a query that will select all Skills, joining the Employee and Competency records, but only return one skill per employee, their newest Skill. Using this sample dataset
Skills
======
id employee_id competency_id created
1 1 1 Jan 1
2 2 2 Jan 1
3 1 2 Jan 3
Employees
===========
id first_name last_name
1 Mike Jones
2 Steve Smith
Competencies
============
id title
1 Problem Solving
2 Compassion
I would like to retrieve the following data
Skill.id Skill.employee_id Skill.competency_id Skill.created Employee.id Employee.first_name Employee.last_name Competency.id Competency.title
2 2 2 Jan 1 2 Steve Smith 2 Compassion
3 1 2 Jan 3 1 Mike Jones 2 Compassion
I was able to select the employee_id and max created using
SELECT MAX(created) as created, employee_id FROM skills GROUP BY employee_id
But when I start to add more fields in the select statement or add in a join I get the 'Column 'xyz' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.' error.
Any help is appreciated and I don't have to use GROUP BY, it's just what I'm familiar with.
The error that you were getting is because SQL Server requires any item in the SELECT list to be included in the GROUP BY if there is an aggregate function being used.
The problem with that is you might have unique values in some columns which can throw off the result. So you will want to rewrite the query to use one of the following:
You can use a subquery to get this result. This gets the max(created) in a subquery and then you use that result to get the correct employee record:
select s.id SkillId,
s.employee_id,
s.competency_id,
s.created,
e.id employee,
e.first_name,
e.last_name,
c.id competency,
c.title
from Employees e
left join Skills s
on e.id = s.employee_id
inner join
(
SELECT MAX(created) as created, employee_id
FROM skills
GROUP BY employee_id
) s1
on s.employee_id = s1.employee_id
and s.created = s1.created
left join Competencies c
on s.competency_id = c.id
See SQL Fiddle with Demo
Or another way to do this is to use row_number():
select *
from
(
select s.id SkillId,
s.employee_id,
s.competency_id,
s.created,
e.id employee,
e.first_name,
e.last_name,
c.id competency,
c.title,
row_number() over(partition by s.employee_id
order by s.created desc) rn
from Employees e
left join Skills s
on e.id = s.employee_id
left join Competencies c
on s.competency_id = c.id
) src
where rn = 1
See SQL Fiddle with Demo
For every non-aggregated column you add to your SELECT statement you need to update your GROUP BY to include it.
This article may help you understand why.
;WITH
MAX_SKILL_created AS
(
SELECT
MAX(skills.created) as created,
skills.employee_id
FROM
skills
GROUP BY
skills.employee_id
),
MAX_SKILL_id AS
(
SELECT
MAX(skills.id) as id,
skills.employee_id
FROM
skills
INNER JOIN MAX_SKILL_created
ON MAX_SKILL_created.employee_id = skills.employee_id
AND MAX_SKILL_created.created = skills.created
GROUP BY
skills.employee_id
)
SELECT
* -- type all your columns here
FROM
employees
INNER JOIN MAX_SKILL_id
ON MAX_SKILL_id.employee_id = employees.employee_id
INNER JOIN skills
ON skills.id = MAX_SKILL_id.id
INNER JOIN competencies
ON competencies.id = skills.competency_id
If you are using SQL Server than you can use OUTER APPLY
SELECT *
FROM employees E
OUTER APPLY (
SELECT TOP 1 *
FROM skills
WHERE employee_id = E.id
ORDER BY created DESC
) S
INNER JOIN competencies C
ON C.id = S.competency_id