In my code I am trying to combine two data tables, Employee and Department. I tried to write a query to print the respective Department Name and number of employees for all departments, even the unstaffed ones. My query looks like this:
SELECT department.name, count(department.name) AS CountOfNAME
FROM department LEFT JOIN employee ON department.dept_id = employee.dept_id
GROUP BY department.name
ORDER BY Count(department.name) DESC, department.name ASC;
And the result is:
Engineering 5
Recruitment 5
Sales 3
Product 2
Finance 1
Operations 1
Research&Development 1
This code works in that it orders departments by number of employees, and then alphabetically, but Finance and Research&Development are not supposed to have any people in them. Is there any way to correctly display those results as having 0 employees? It seems to be a hard thing to do in SQL because of how join works.
The COUNT function should ignore NULL values, giving you a zero count for the finance and research departments. The problem is that you are counting a column in the department table, which always will be non NULL due to that this table is on the left side of the LEFT JOIN. Instead, try counting a column in the employee table:
SELECT department.name,
COUNT(employee.dept_id) AS CountOfNAME
FROM department
LEFT JOIN employee
ON department.dept_id = employee.dept_id
GROUP BY department.name
ORDER BY COUNT(employee.dept_id) DESC,
department.name ASC;
I suggest you create a view for the tallies of employees by department e.g.
CREATE VIEW DepartmentEmployeeTallies
AS
SELECT dept_id, COUNT(*) AS tally
FROM employee
UNION
SELECT dept_id, 0 AS tally
FROM department
WHERE dept_id NOT IN ( SELECT dept_id FROM employee );
Then things resolve to a simple join:
SELECT name, tally
FROM department
NATURAL JOIN
DepartmentEmployeeTallies;
Related
I have two tables, one is departments and the other is employees. The department id is a foreign key in the employees table. The employee table has a name and a flag saying if the person is part-time. I can have zero or more employees in a department. I'm trying to figure out out to get a list of all departments where a department has at least one employee and if it does have at least one employee, that all the employees are part time. I think this has to be some kind of subquery to get this. Here's what I have so far:
SELECT dept.name
,dept.id
,employee.deptid
,count(employee.is_parttime)
FROM employee
,dept
WHERE dept.id = employee.deptid
AND employee.is_parttime = 1
GROUP BY employee.is_parttime
I would really appreciate any help at this point.
You must join (properly) the tables and group by department with a condition in the HAVING clause:
select d.name, d.id, count(e.id) total
from dept d inner join employee e
on d.id = e.deptid
group by d.name, d.id
having total = sum(e.is_parttime)
The inner join returns only departments with at least 1 employee.
The column is_parttime (I guess) is a flag with values 0 or 1 so by summing it the result is the number of employees that are part time in the department and this number is compared to the total number of employees of the department.
As a preliminary aside, I recommend expressing joins with the JOIN keyword, and segregating join conditions from filter conditions. Doing so would make the original query look like so:
select dept.name, dept.id, employee.deptid, count(employee.is_parttime)
from employee
join dept on dept.id = employee.deptid
where employee.is_parttime = 1
group by employee.is_parttime
It doesn't make much practical difference for inner joins, but it does make the structure of the data and the logic of the query a bit clearer. On the other hand, it does make a difference for outer joins, and there is value in consistency.
As for the actual question, yes, one can rewrite the original query using a subquery or an inline view to produce the requested result. (An "inline view" is technically what one should call an embedded query used as a table in the FROM clause, but some people lump these in with subqueries.)
Example using a subquery
select dept.name, dept.id
from dept
where dept.id in (
select deptid
from employee
group by deptid
having count(*) == sum(is_parttime)
)
Example using an inline view
select dept.name, dept.id
from dept
join (
select deptid
from employee
group by deptid
having count(*) == sum(is_parttime)
) pt_dept
on dept.id = pt_dept.deptid
In each case, the subquery / inline view does most of the work. It aggregates employees by department, then filters the groups (HAVING clause) to select only those in which the part-time employee count is the same as the total count. Naturally, departments without any employees will not be represented. If a list of department IDs would suffice for a list of departments, then that's actually all you need. To get the department names too, however, you need to combine that with data from the dept table, as demonstrated in the two example queries.
I have two tables: Employee and Department
Fields of Employee : id, name, dept_id
Fields of Department: dept_id, name
I need a query which fetches me department name and number of employees per department along with it, even if number of employees for a department is zero.
I tried the query below, but it returns only those departments which have number of employees more than zero.
select d.name, count(e.id) as "Number of employees per department"
from department d, employee e
where d.DEPT_ID=e.DEPT_ID
group by d.name
order by count(e.id) desc;
Please help.
You need to outer join your tables.
FROM department d
LEFT JOIN employee e
ON d.DEPT_ID=e.DEPT_ID
The following query gives the desired result.
SELECT DEPARTMENT.NAME,COUNT(EMPLOYEE.DEPT_ID)
FROM DEPARTMENT LEFT OUTER JOIN EMPLOYEE ON EMPLOYEE.DEPT_ID = DEPARTMENT.DEPT_ID GROUP BY DEPARTMENT.NAME ;
I'm new to SQL and trying to solve this problem with two tables Employee and Department. I want display the names of all the employees of 'HR' and 'Sales' departments.
Tables are Employee (emp_id, emp_name, dept_id) and Department (dept_id, dept_name).
Thanks!
Try this:
Select e.emp_name from
Employee e inner join Department d
on e.dept_id = d.dept_id
Where d.dept_name in ('HR','Sales');
This query will compare the dept_id of Employee table and Department table. Those values matched will be returned. then out of all the fields you will select the emp_name and limit the employees belonging to HR and Sales department using the where clause.
As you only want to display employee data, only select from that table. The rest is criteria. You want their department to be either 'HR' or 'Sales', so the straight-forward way of writing this is the IN clause (you could also use the EXISTS clause):
select emp_name
from employee
where dept_id in
(
select dept_id
from department
where dept_name in ('HR', 'Sales')
);
I think this is more easy to read than to join the tables first, only to use one as a filter for the other.
select Employee.emp_name [Emplyee Name] from Employee inner join Department on Department.dept_id=Emplyee.emp_id where Department.dept_name='HR'
You can get like this,
SELECT [emp_name] FROM [dbo].[Employee] AS E
INNER JOIN [dbo].[Department] AS D
ON D.dept_id=E.dept_id
WHERE D.dept_name='HR' OR D.dept_name='Sales'
Lets say I have the following database model:
And the question is as follows:
List ALL department names and the total number of employees in the department. The total number of employees column should be renamed as "total_emps". Order the list from the department with the least number of employees to the most number of employees. Note: You need to include a department in the list even when the department does not currently have any employee assigned to it.
This was my attempt:
SELECT Department.deptname
(SELECT COUNT(*)
FROM Department
WHERE Department.empno = Employee.empno ) AS total_emps
FROM Department
I'm pretty sure my solution is not correct as it won't include departments with no employees. How do you use a left inner join to solve this problem?
The query as you were trying to write it is:
(table creates modified from shree.pat18's sqlfiddle to this sqlfiddle)
create table department (deptno int, deptname varchar(20));
insert into department values (1, 'a'),(2, 'b'),(3, 'c');
create table employee (empno int, deptno int);
insert into employee values (1,1),(2,1),(3,3);
SELECT d.deptname,
(SELECT COUNT(*)
FROM EMPLOYEE e
WHERE d.deptno = e.deptno ) AS total_emps
FROM DEPARTMENT d
ORDER BY total_emps ASC;
(You were counting from DEPARTMENT instead of EMPLOYEE and comparing empno instead of deptno. And you left out a comma.)
(You were asked for every department's name and employee count so this returns that. In practice we would include a presumably unique deptno if deptname was not unique.)
I'm pretty sure my solution is not correct as it won't include
departments with no employees.
Even your answer's version of the query (with the missing comma added) has an outer select that returns a count for every department no matter what the subselect returns. So I don't know why/how you thought it wouldn't.
If you want to use LEFT (OUTER) JOIN then the DEPARTMENT rows with no employees get extended by NULL. But COUNT of a column only counts non-NULL rows.
SELECT d.deptname, COUNT(e.empno) AS total_emps
FROM DEPARTMENT d
LEFT JOIN EMPLOYEE e
ON d.deptno = e.deptno
GROUP BY d.deptno
ORDER BY total_emps ASC;
(Nb the LEFT JOIN version uses more concepts: LEFT JOIN extending by NULL, GROUP BY, and COUNT's NULL behaviour for non-*.)
First off, it's a left outer join. Now, for your query, you want to join the 2 tables based on deptno, then also group by deptno (or deptname, since that is as likely to be unique) to ensure that any aggregation we do is done for each unique department in the table. Finally, the counting is done with the count function, leading to this query:
select d.deptname, count(e.empno) as total_emps
from department d
left join employee e on d.deptno = e.deptno
group by d.deptname
SQLFiddle
Note that since we want all records from department regardless of whether there are matching records in employee or not, department must appear at the left side of the join. We could have done the same thing using a right outer join by swapping the positions of the 2 tables in the join.
I have a table with 2 columns, say Empname, DepartmentName where one employee may work in multiple departments and vice-versa.
I want to write a query to select the EmpName based on some departments combinations.
For ex. The employee who works under Dept1, Dept2 and Dept3 only (it should select only those employees who works under only 3 departments, in this case)
Here is the query I tried.
Select EmpName, Count(*) as Total from Emp_Departments where DepartmentName in ('Dept1', 'Dept2', 'Dept3') group by EmpName having Total=3
Here the problem is, Any employees who are working any of the above mentioned departments and their total count is 3, it returns their rows also.
Please suggest how can I get the unique combinations only.
You were close - use:
SELECT d.EmpName, COUNT(*) as Total
FROM Emp_Departments d
WHERE d.DepartmentName IN ('Dept1', 'Dept2', 'Dept3')
GROUP BY d.EmpName
HAVING COUNT(DISTINCT d.DepartmentName) = 3