Retrieve the names of employees who is making least in their departments - sql

I have two tables
EMPLOYEE (Fname, Lname, Ssn, Salary, Dno)
DEPARTMENT (Dname, Dno, Location)
I want to list the names of all employees making the least in their department
I have come up with this
select min(E.Salary) from EMPLOYEE E group by E.Dno;
but how do I join the EMPLOYEE table with it and display the 'Fname' and 'Lname';

Analytic functions would be best but this would also work:
select *
from employee e
where salary = (select min(x.salary) from employee x where x.dno = e.dno)

Use Analytic function, ROW_NUMBER() OVER( PARTITION BY DNO ORDER BY SALARY) as RN. So, WHERE RN = 1 will give you the employee with least salary in each department.
Remember, if there are two employees with same salary, then you need to use DENSE_RANK to avoid similar rank.
Note : This answer is for Oracle.

A simple way to do it is to check that no one with a lower salary exists in the same department;
SELECT e1.*
FROM employee e1
WHERE NOT EXISTS(
SELECT 1 FROM employee e2 WHERE e1.dno = e2.dno AND e1.salary > e2.salary
);
An SQLfiddle to test with.

Related

Nested query sql hospital

enter image description here
select Fname,Lname,salary,ssn
from Employee
where joptype='nurse' AND salary <= ALL (select salary
from Employee E,Nurses N
where E.Ssn=N.Ssn AND E.joptype='nurse' AND N.shift='morning')
I want to return a list of nurses name who works at morning and their salary is less than all nurses, or the opposite. I've tried both morning and night, also I've tried greater than all nurses. I think the problem is in the second where it seems like he ignores the shift condition.
You can use EXISTS to find the morning shifts and, from Oracle 12, you can use ORDER BY salary ASC FETCH FIRST ROW WITH TIES to find the matching employees with the lowest salary:
SELECT Fname,
Lname,
salary,
ssn
FROM Employee e
WHERE jobtype='nurse'
AND EXISTS( SELECT 1
FROM Nurses N
WHERE E.Ssn=N.Ssn
AND N.shift='morning' )
ORDER BY salary ASC
FETCH FIRST ROW WITH TIES;
or, if you want to check all nurses and find the minimum salary and then filter on morning shifts:
SELECT *
FROM (
SELECT Fname,
Lname,
salary,
ssn
FROM Employee
WHERE jobtype='nurse'
ORDER BY salary ASC
FETCH FIRST ROW WITH TIES
) e
WHERE EXISTS( SELECT 1
FROM Nurses N
WHERE E.Ssn=N.Ssn
AND N.shift='morning' )
If you want to use a JOIN, rather than EXISTS, then you can use:
SELECT e.Fname,
e.Lname,
e.salary,
e.ssn
FROM (
SELECT Fname,
Lname,
salary,
ssn
FROM Employee
WHERE jobtype='nurse'
ORDER BY salary ASC
FETCH FIRST ROW WITH TIES
) e
INNER JOIN Nurses N
ON E.Ssn=N.Ssn
WHERE N.shift='morning'
(However, if there are multiple entries in Nurses for an Employee then you will get duplicates using a JOIN that you would not get using EXISTS)
Or for your code, you appear to have the filter for shifts in the wrong place:
SELECT e.Fname,
e.Lname,
e.salary,
e.ssn
FROM Employee e
INNER JOIN Nurses n
ON (e.Ssn = n.Ssn)
WHERE e.jobtype='nurse'
AND n.shift='morning'
AND e.salary <= ALL ( SELECT salary
FROM Employee
WHERE jobtype='nurse' );
fiddle
This is how I understood it; what you said:
their salary is less than all nurses
probably means their average salary, so I used it in a subquery.
select e.fname, e.lname, e.salary, e.ssn
from employee e join nurse n on e.ssn = n.ssn
where e.joptype = 'nurse'
and n.shift = 'morning'
and salary < (select avg(salary) --> average salary of all nurses
from employee
where joptype = 'nurse'
);
(Just a remark: is it really joptype? Is it not a job?)
Maybe you could try it like here:
select
Fname, Lname, salary, ssn
from
Employee
where
joptype='nurse' AND
salary <= ( Select MIN(salary)
From Employee E
Inner join Nurses N ON(E.Ssn=N.Ssn)
Where E.joptype = 'nurse' AND
N.shift = 'morning'
)
... this will give you the list of nurses with salary less than or equal to the minimum salary among morning nurses..

Query to display employees with lowest salary from each department

I want to display the lowest earning employees of each department based on salary using min().
I have tables of employees with id, first name, last name, department id, salary
and departments, department_id, name department id from 1 to 5.
I am having trouble doing this, I only know how to start
SELECT name, surname from employees WHERE...
You would use min() for this. You would use window functions:
select e.*
from (select e.*,
rank() over (partition by department_id order by salary) as seqnum
from employees e
) e
where seqnum = 1
order by department_id;
With rank()over() ranking window function you can have your rows within a group ranked as you wish. Here we have ranked all the employees starting with lowest salary in a department. Now if we select rows with rn =1 then it will select employees from a department with lowest salary.
Then joined the result with department table to get the name of the
With lowestEarningEmployees as
(
id, first_name, last_name, department_id, salary, rank()over(partition by department_id order by salary)rn from employees
)
select le.id, le.first_name, le.last_name, le.department_id,d.name, le.salary,
from lowestEarningEmployees le inner join departments d on le.department_id=d.department_id
** If more than one employee in a department have lowest salary all of them will be selected. If you want to select only one employee with lowest salary then you need to use row_number() instead of rank().
You can also use subquery to have your desired result (Though I would suggest to use first one) :
Select e.id, e.first_name, e.last_name, e.department_id, d.name, e.salary
from employees e inner join department d on e.department_id = d.department_id
where e.salary=(select min(salary) from employees empl where e.department_id=empl.department_id)

Max Salary with a single GroupBy without Joins

Schema for EMPLOYEE
(ID, EMPLOYEENAME, SALARY, ORGANIZATIONID)
Query to Solve: Find employee Names in each organization with Maximum Salary without a Join.
SELECT E.*
FROM EMPLOYEE E,
(SELECT EMP.ORGANIZATIONID, MAX(EMP.SALARY)
FROM EMPLOYEE EMP
GROUP BY EMP.ORGANIZATIONID) MAXSALARY
WHERE MAXSALARY.SALARY =E.SALARY
AND E.ORGANIZATIONID=EMP.ORGANIZATIONID ;
Is there a way to avoid the join? I am using Spark SQL API and joins cause an extra shuffle operation which is expensive. Is there a way to get the employee name while getting the max salary?
Assume you have a single employee in each organization having the max salary
You can use PARTITION BY with Spark SQL as shown below (Although it will require a subquery)
SELECT E.*
FROM
(SELECT EMP.EMPLOYEENAME, EMP.ORGANIZATIONID, EMP.SALARY,
row_number() OVER (PARTITION BY ORGANIZATIONID ORDER BY SALARY DESC) as rank
FROM EMPLOYEE EMP
) AS E
WHERE E.rank=1
Try this:
SELECT P.ORGANIZATIONID, P.EMPLOYEENAME
FROM EMPLOYEE P
WHERE P.SALARY = (SELECT MAX(E.SALARY) FROM EMPLOYEE E WHERE P.ORGANIZATIONID = E.ORGANIZATIONID)
GROUP BY P.ORGANIZATIONID, P.EMPLOYEENAME
Try this:
SELECT EMPLOYEENAME FROM EMPLOYEE
WHERE SALARY IN (SELECT MAX(SALARY) FROM EMPLOYEE GROUP BY ORGANIZATIONID)

Employee that has a higher salary than the AVERAGE of his department - optimized

We have only a table named EMPLOYEESALARY in our database with the 3 following columns:
Employee_ID, Employee_Salary, Department_ID
Now I have to SELECT every employee that has a higher salary than the AVERAGE of his department. How do I do that?
I know this is a repeat question but the best solution I found everywere was:
SELECT * from employee join (SELECT AVG(employee_salary) as sal, department_ID
FROM employee GROUP BY Department_ID) as t1
ON employee.department_ID = t1.department_ID
where employee.employee_salary > t1.sal
Can we optimize it further and do it without a subquery?
Reference:
SELECT every employee that has a higher salary than the AVERAGE of his department
Employees with higher salary than their department average?
Find Schema here, to test: SQL Fiddle
Can we do it without a subquery?
Not that I can think of. Had the condition been >= then the following would have worked
SELECT TOP 1 WITH TIES *
FROM employee
ORDER BY CASE
WHEN employee_salary >= AVG(employee_salary)
OVER (
PARTITION BY Department_ID) THEN 0
ELSE 1
END
But this is not an optimisation and it won't work correctly for the > condition if no employee has a salary greater than the average anyway (i.e. all employees in a department had the same salary)
Can we optimize it further?
You could shorten the syntax a bit with
WITH T AS
(
SELECT *,
AVG(employee_salary) OVER (PARTITION BY Department_ID) AS sal
FROM employee
)
SELECT *
FROM T
WHERE employee_salary > sal
but it still has to do much the same work.
Assuming suitable indexes on the base table already exist then the only way of avoiding some more of that work at SELECT time would be to pre-calculate the grouped SUM and COUNT_BIG in an indexed view grouped by Department_ID (to allow the average to be cheaply derived) .
A more optimal form is likely to be:
select e.*
from (select e.*, avg(employee_salary) over (partition by department_id) as avgs
from employee e
) e
where employee_salary > avgs;
This (as well as other versions) can use an index on employee(department_id, employee_salary). The final where probably should not use an index, because it is selecting lots of rows.

Using the MIN function in the having clause

I want to get the name of the employee who has the minimum salary. Is there a way to do this using only one query? I have given my query below, it doesn't work because the having clause requires a condition. Is there any way to give a condition in the having clause that will retreive the employee name with the minimum salary?
SELECT first_name,min(salary) as "sal"
FROM Employees
GROUP BY first_name
having min(salary);
How about using ROWNUM?
SELECT *
FROM(SELECT first_name, salary
FROM Employees
ORDER BY salary
) WHERE ROWNUM = 1
SELECT first_name, salary as "sal"
FROM employees
WHERE salary =(SELECT MIN(salary)
FROM employees);
Try this solution, inspired from here:
SELECT e1.first_name, e1.salary AS "sal"
FROM Employees e1
LEFT OUTER JOIN Employees e2
ON (e1.id <> e2.id AND e1.salary > e2.salary)
WHERE e2.id IS NULL;
With a single SELECT statement:
SELECT MIN( first_name ) KEEP ( DENSE_RANK FIRST ORDER BY salary ASC, first_name ASC ) AS first_name,
MIN( salary ) KEEP ( DENSE_RANK FIRST ORDER BY salary ASC, first_name ASC ) AS salary
FROM Employees;
SQLFIDDLE
However, if there are multiple people with the same minimum salary then this will only get the one with the name which is first alphabetically.
You can get all the names, but it does require multiple SELECT statements:
SELECT first_name, salary
FROM Employees
WHERE salary = ( SELECT MIN(salary) FROM Employees );
But having multiple SELECT statements isn't a bad thing.
SQLFIDDLE
SELECT TOP 1 WITH TIES *
FROM employees
ORDER BY salary ASC
If you need the employee with the lowest salary why don't you use Order By ..
SELECT top 1 first_name,min(salary) as LowestSalary
FROM Employees order by Salary asc
SELECT first_name,min(salary) as "sal"
FROM Employees
GROUP BY first_name
having min(salary) >0;
If you want to do this with only one query, while also retrieving all employees with the minimum salary (example: you have a minimum salary of $40,000, but two employees have this exact salary) you could join the table with itself. This solution also uses the 'having' clause that you included in your original question.
SELECT e.first_name,e.salary AS "sal"
FROM Employees e, Employees e2
GROUP BY first_name
HAVING MIN(e2.salary)=e.salary;