Using Min aggregate function on Count aggregate function - sql

I used this query:
select D.DeptID, D.Dept, count(E.EmployeeID) as TotalStaff
from Employees as E
right join Departments as D
on D.DeptID = E.DeptID
group by D.DeptID, D.Dept;
To return this:
DeptID_|_Dept___________|TotalStaff
40 | Marketing | 2
50 | Accounting | 3
60 | Manager | 3
70 | GeneralStaff | 1
80 | HumanResources | 1
90 | Production | 0
100 | Sales | 0
Now I want to list the department ID, department and the number of employees for the department that has the lowest number of employees so I tried this:
SELECT MIN(mycount)
FROM
(
select D.DeptID, count(E.EmployeeID) as mycount
from Employees as E
right join Departments as D
on D.DeptID = E.DeptID
group by D.DeptID
);
But I get an error that states: Incorrect syntax near ';'.
All I want to do is return the department that has the lowest amount of employees. Please could anyone help me with this.

please try with below query:
For minimum value:
select min(A.mycount) as min_count
from (
select D.DeptID, count(E.EmployeeID) as mycount
from Departments as D
left outer join Employees as E on D.DeptID = E.DeptID
group by D.DeptID
) as A
For maximum value:
select max(A.mycount) as max_count
from (
select D.DeptID, count(E.EmployeeID) as mycount
from Departments as D
left outer join Employees as E on D.DeptID = E.DeptID
group by D.DeptID
) as A

You have missed the subquery alias
So, your subquery has alias like that
SELECT MIN(mycount)
FROM (select D.DeptID, count(E.EmployeeID) as mycount
from Employees as E
right join Departments as D on D.DeptID = E.DeptID
group by D.DeptID
) s; -- alias missed

The normal way to write this query is to use the ANSI standard rank() function:
select d.*
from (select D.DeptID, D.Dept, count(E.EmployeeID) as TotalStaff,
rank() over (order by count(E.EmployeeID) asc) as seqnum
from Departments d left join
Employees E
on D.DeptID = E.DeptID
group by D.DeptID, D.Dept
) d
where seqnum = 1;
Notice that I also switched the JOIN to a LEFT JOIN. LEFT JOIN is generally simpler to follow (at least for people who read languages left-to-right) because it says to keep all rows in the first table rather than the last table.

try adding
as tablename
after your last ) and before the final ;

Related

Can't find no employees in each department

DEPARTMENT Table
DEPARTMENT_ID
| 1 |
| 2 |
EMPLOYEES Table
EMPLOYEE_ID | DEPARTMENT_ID
ANDY | 1
Output
DEPARTMENT_ID|
2
Here is my code:
SELECT DEPARTMENT_ID
FROM DEPARTMENTS
where department_id!= all ( SELECT department_id
FROM employees
);
The code doesn't show 2,and the output is blank.
Like this:
DEPARTMENT_ID|
If you want to find the number of each employee in each department, you should use a left join aggregation:
SELECT d.DEPARTMENT_ID, COUNT(e.EMPLOYEE_ID) AS cnt
FROM DEPARTMENT d
LEFT JOIN EMPLOYEES e
ON d.DEPARTMENT_ID = e.DEPARTMENT_ID
GROUP BY d.DEPARTMENT_ID;
The above count expression counts the EMPLOYEE_ID column in the employee table. It is important to note that should a department have no employees, then COUNT would return zero, since NULL is not counted.
SELECT d.department_id
FROM departments d
LEFT JOIN employees e ON d.department_id = e.department_id
WHERE e.department_id IS NULL
SQLFiddle Demo

An SQL query to pull count of employees absent under each manager on all dates

The objective of the query is get a count of employees absent under each manager.
Attendance (Dates when employees are present)
id date
1 16/05/2020
2 16/05/2020
1 17/05/2020
2 18/05/2020
3 18/05/2020
Employee
id manager_id
1 2
2 3
3 NA
The desired output should be in this format:
Date manager_id Number_of_absent_employees
16/05/2020 NA 1
17/05/2020 3 1
17/05/2020 NA 1
18/05/2020 2 1
I have tried writing code but partially understood it, intuition being calculating total number of actual employees under each manager and subtracting it from number of employees present on given day. Please help me in completing this query, many thanks!
with t1 as /* for counting total employees under each manager */
(
select employee.manager_id,count(*) as totalc
from employee as e
inner join employee on e.employee_id=employee.employee_id
group by employee.manager_id
)
,t2 as /* for counting total employees present each day */
(
select Attendence.date, employee.manager_id,count(*) as present
from employee
Left join Attendence on employee.employee_id=Attendence.employee_id
group by Attendence.date, employee.manager_id
)
select * from t2
Left join t1 on t2.manager_id=t1.manager_id
order by date
Cross join the distinct dates from Attendance to Employee and left join Attendance to filter out the matching rows.
The remaining rows are the absences so then you need to aggregate:
select d.date, e.manager_id,
count(*) Number_of_absent_employees
from (select distinct date from Attendance) d
cross join Employee e
left join Attendance a on a.date = d.date and a.id = e.id
where a.id is null
group by d.date, e.manager_id
See the demo.
Results:
| date | manager_id | Number_of_absent_employees |
| ---------- | ---------- | -------------------------- |
| 16/05/2020 | NA | 1 |
| 17/05/2020 | 3 | 1 |
| 17/05/2020 | NA | 1 |
| 18/05/2020 | 2 | 1 |
Try this query. In first cte just simplify your code. And in the last query calculate absent employees.
--in this CTE just simplify counting
with t1 as /* for counting total employees under each manager */
(
select employee.manager_id,count(*) as totalc
from employee
group by manager_id
)
,t2 as
(
select Attendence.date, employee.manager_id,count(*) as present
from employee
Left join Attendence on employee.employee_id=Attendence.employee_id
group by Attendence.date, employee.manager_id
)
select t2.date,t2.manager_id, (t1.totalc-t2.present) as employees_absent from t2
Left join t1 on t2.manager_id=t1.manager_id
order by date
Select ec.manager_id, date, (total_employees - employee_attended) as employees_absent from
(Select manager_id, count(id) as total_employees
from employee
group by manager_id) ec,
(Select distinct e.manager_id, a.date, count(a.id) over (partition by e.manager_id, a.date) as employee_attended
from Employee e, attendence, a
where e.id = a.id(+)) ea
where ec.manager_id = ea.manager_id (+)
I guess this should work

I want to get data from 3 tables

I have 3 tables employee, jobs and department as below,
------------------- ---------------- ------------------
| employee | | jobs | | department |
------------------- ---------------- ------------------
| empId | | jobId | | deptId |
| fname | | jobTitle | | deptName |
| lname | | | | managerId |
| jobId | | | | |
| managerId | | | | |
| departmentId | | | | |
------------------- ---------------- -------------------
I want to select all data from employee, the job title through jobId, the deptName through deptId and manager name through managerId and employeeId
SELECT EMPLOYEES.EMPLOYEE_ID, EMPLOYEES.FIRST_NAME, EMPLOYEES.LAST_NAME,
EMPLOYEES.JOB_ID, JOBS.JOB_TITLE AS JOB_TITLE, EMPLOYEES.SALARY,
DEPARTMENTS.DEPARTMENT_ID, DEPARTMENTS.DEPARTMENT_NAME AS DEPARTMENT_NAME
FROM EMPLOYEES
LEFT JOIN JOBS ON EMPLOYEES.JOB_ID = JOBS.JOB_ID
LEFT JOIN DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
what should I do to get the name of the manager using the empId?
or is there any other way to simplify this block of code?
Just Add another LEFT JOIN to the EMPLOYEES table, but give it an alias, say MANAGERS.
SELECT
EMPLOYEES.EMPLOYEE_ID,
EMPLOYEES.FIRST_NAME,
EMPLOYEES.LAST_NAME,
EMPLOYEES.JOB_ID,
JOBS.JOB_TITLE AS JOB_TITLE,
EMPLOYEES.SALARY,
DEPARTMENTS.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME AS DEPARTMENT_NAME,
MANAGERS.FIRST_NAME AS MANAGER_FIRST_NAME,
MANAGERS.LAST_NAME AS MANAGER_LAST_NAME
FROM EMPLOYEES
LEFT JOIN EMPLOYEES MANAGERS
ON EMPLOYEES.MANAGER_ID = MANAGERS.EMPLOYEE_ID
LEFT JOIN JOBS
ON EMPLOYEES.JOB_ID = JOBS.JOB_ID
LEFT JOIN DEPARTMENTS
ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
You can use another left join on employee table to get the manager name.
SELECT (
EMPLOYEES.EMPLOYEE_ID,
EMPLOYEES.FIRST_NAME,
EMPLOYEES.LAST_NAME,
EMPLOYEES.JOB_ID,
JOBS.JOB_TITLE AS JOB_TITLE,
EMPLOYEES.SALARY,
DEPARTMENTS.DEPARTMENT_ID,
DEPARTMENTS.DEPARTMENT_NAME AS DEPARTMENT_NAME,
(MGR.FIRST_NAME + ' ' + MGR.LAST_NAME) AS MANAGER_NAME)
FROM EMPLOYEES
LEFT JOIN JOBS
ON EMPLOYEES.JOB_ID = JOBS.JOB_ID
LEFT JOIN DEPARTMENTS
ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
LEFT JOIN EMPLOYEES MGR
ON EMPLOYEES.MANAGERID = MGR.EMPLOYEE_ID
try using aliases and join back to employee on the manager id.
SELECT
E.EMPLOYEE_ID, E.FIRST_NAME, E.LAST_NAME,
E.JOB_ID, J.JOB_TITLE AS JOB_TITLE, E.SALARY,
D.DEPARTMENT_ID, D.DEPARTMENT_NAME AS DEPARTMENT_NAME,
M.EMPLOYEE_ID as mgr_id, M.FIRST_NAME as mgr_name, M.LAST_NAME as mgr_lname
FROM EMPLOYEES E LEFT JOIN JOBS J ON
E.JOB_ID = J.JOB_ID
LEFT JOIN DEPARTMENTS D ON
E.DEPARTMENT_ID = D.DEPARTMENT_ID
join employees M ON
E.manager_ID = M.employee_ID
The reason this query works is because I am doing a self join back to the Employees table from the main result set that you already established. To put it simply, imagine you made an exact copy of the employees table and called it M. If you took your original query and joined to the M table using the original query's Employee.manager_id to the employee_ID in the M table, then you would have the manager for the employee.
There is no need to create an exact copy of the employee table just to look up the manager. We can just reference the employee table a second time and use an alias (I used M for manager). Then we join from your list of employees using the manager_id to get the employee's manager.
You could do this again to get the manager's manager if you need to. Here is that query:
SELECT
E.EMPLOYEE_ID, E.FIRST_NAME, E.LAST_NAME,
E.JOB_ID, J.JOB_TITLE AS JOB_TITLE, E.SALARY,
D.DEPARTMENT_ID, D.DEPARTMENT_NAME AS DEPARTMENT_NAME,
M.EMPLOYEE_ID as mgr_id, M.FIRST_NAME as mgr_name, M.LAST_NAME as mgr_lname
FROM EMPLOYEES E LEFT JOIN JOBS J ON
E.JOB_ID = J.JOB_ID
LEFT JOIN DEPARTMENTS D ON
E.DEPARTMENT_ID = D.DEPARTMENT_ID
join employees M ON /* The employee's manager */
E.manager_ID = M.employee_ID
LEFT join employees MM ON /* The employee's manager's manager */
M.manager_ID = MM.employee_ID
I used a left join for this last one, because at some point you will get to the top of the management hierarchy and might find that there are no more managers. You could also put a left join on the join employees M
Here it is in Tabular form
Employee_id | Name | manager_id
1 | Fred | 10
2 | Jane | 10
10 | Bob | 20
20 | Betty | Null
Looking at employee # 1. The values of E.employee_id = 1, E.Name = Fred and E.manager_id = 10.
So the relevant lines of the query evaluate as follows:
...
join employees M ON /* The employee's manager */
E.manager_ID /* i.e. 10 */ = M.employee_ID
...
So the M alias now refers to the employee record where M.employee_ID = 10 and as such, M.Name = Bob and M.employee_id = 20.
Using the last version of the query, we could then work out that Fred's manager's manager (i.e. Fred's manager is Bob and Bob's manager is Betty) will have an employee_id of 20 (i.e. M.manager_ID = 20), so the MM.employee_id would be 20 and hence refer to Betty who doesn't seem to have a manager.

PostgreSQL find the minimum

I have 2 tables:
eomployee:
e_id | firstname | lastname
-------+-----------+-----------
10100 | Mark | Stevens
10101 | Alex | Watts
10102 | Hannah | Burton
and works_on:
employee_e_id | product_prod_nr | hours
---------------+-----------------+-------
10100 | 66000 | 40
10100 | 77211 | 37
10101 | 90210 | 67
I now want to get the e_id, firstname, lastname, hours of the person who is working the least hours. This is just sample data from the 2 tables.
I tried ti get it with limit 1, but that does not work when i have 2 people working 1 hour for example.
You can use window functions:
SELECT e_Id, firstname, lastname, hours
FROM (SELECT e.*,
MIN(hours) OVER () as min_hours
FROM eomployee e JOIN
works_on w
ON e.e_id = w.employee_e_id
) ew
WHERE hours = min_hours;
try like below by using window function
with cte as
(
select e.e_id,
e.firstname,e.lastname,w.hours,row_number() over(order by w.hours asc) rn
from eomployee e join works_on w on e.e_id=w.employee_e_id
) select * from cte where rn=1
Try this:
SELECT e.*,
max_hours_row.*
FROM eomployee e
INNER JOIN
(SELECT employee_e_id,
MIN(hours) AS time_min
FROM works_on
GROUP BY employee_e_id ) AS max_hours_row ON max_hours_row.employee_e_id = e.e_id
If you want the row with the least hours (= 37 in your example):
SELECT
e.e_id,
e.firstname,
e.lastname,
w.hours
FROM
eomployee e
JOIN works_on w ON e.e_id = w.employee_e_id
ORDER BY w.hours
LIMIT 1
If you want to get the row for cumulative working hours (in your example "10100" is working 37+ 40 = 77 hours):
SELECT
e_Id, firstname, lastname, hours
FROM (
SELECT
e.e_id,
e.firstname,
e.lastname,
w.hours,
SUM(hours) OVER (PARTITION BY e_id) as cum
FROM
eomployee e
JOIN works_on w ON e.e_id = w.employee_e_id
) s
ORDER BY cum
LIMIT 1
demo:db<>fiddle

SQL want Repeated result NULL from one table

hi i have problem in sql query example
Employee
empid empname
1 gan
2 sam
Designation
id desig empid
1 sr officerr 1
2 jr officer 1
3 manager 2
i want join tables and want Employee Table repeated records Null
result like
empid name desig id
1 gan sr officerr 1
1 NULL jr officer 2
2 sam manager 3
i working on query but i not getting result
SELECT DISTINCT designatin.empid, employee.empname,designatin.desig
FROM designatin INNER JOIN employee e ON employee.empid = designatin.empid
GROUP BY employee.empid, employee.empname, designatin.desig
can anybody have solution?
Change the inner join to a left join:
SELECT DISTINCT designatin.empid, employee.empname,designatin.desig
FROM designatin LEFT JOIN employee e ON employee.empid = designatin.empid
GROUP BY employee.empid, employee.empname, designatin.desig
Let try this, it will help you
SELECT e.empid, e.empname,d.desig ,d.id
FROM employee e
INNER JOIN Designation d ON e.empid = d.empid
See DEMO