I'm working on Oracle SQL and HR database, I'm trying to select maximum salary in department like this:
SELECT MAX(salary), department_id
FROM employees GROUP BY department_id;
It works fine, but I want to know >who< is earning the most, so I simply change query this way:
SELECT first_name, last_name, MAX(salary), department_id
FROM employees GROUP BY department_id;
And it's wrong. Could you help me, please?
The most efficient way to do this sort of analysis is generally to use analytic functions (window functions). Something like
SELECT first_name,
last_name,
salary,
department_id
FROM (SELECT e.*,
rank() over (partition by department_id
order by salary desc) rnk
FROM employees e)
WHERE rnk = 1
Depending on how you want to handle ties (if two people in the department are tied for the maximum salary, for example, do you want both people returned or do you want to return one of the two arbitrarily), you may want to use the row_number or dense_rank functions rather than rank.
Create a view:
create view max_salary as select max(salary), department_id from employees group by department_id
Then create a query:
select first_name, last_name, salary, department_id
from employees a, max_salary b
where a.department_id = b.department_id
and a.salary = b.salary
Related
im solving the following task with analytic functions and im stuck.
task: Write a query that shows the latest hired employee per department. In case of ties, use the lowest employee ID.
select a.EMPLOYEE_ID,
a.DEPARTMENT_ID,
a.FIRST_NAME,
a.LAST_NAME,
a.HIRE_DATE,
a.JOB_ID
from (select ROW_NUMBER() over (PARTITION by department_id order by hire_date desc)
from hr.EMPLOYEES a) A
where A = 1 ;
You need to include the columns you want to select in the outer query in the SELECT clause of the inner query and need to give an alias to the ROW_NUMBER computed value:
select EMPLOYEE_ID,
DEPARTMENT_ID,
FIRST_NAME,
LAST_NAME,
HIRE_DATE,
JOB_ID
from (
select EMPLOYEE_ID,
DEPARTMENT_ID,
FIRST_NAME,
LAST_NAME,
HIRE_DATE,
JOB_ID,
ROW_NUMBER() over (PARTITION by department_id order by hire_date desc) AS rn
from hr.EMPLOYEES
)
where rn = 1 ;
You still need to address the second part of the question:
In case of ties, use the lowest employee ID.
However, since this appears to be a homework question, I'll leave that for you to solve.
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)
I have a practice that I should find the employees who earn more than average salary and works in the departments with employees whose last name contains the letter u
the select statement I have used was
SELECT employee_id,
last_name,
salary
FROM employees
WHERE salary > (SELECT AVG(salary)
FROM employees )
AND department_id IN(SELECT department_id
FROM employees
WHERE LOWER(last_name) LIKE '%u%')
Could anyone check this statement is suitable or not ?
thank you
That looks fine to me, assuming you mean the average salary across all departments in the database, and all employees (active or not) across all of time.
I would think you might be more interested in all active employees in this current financial year, for example.
You haven't provided the schema, so be careful to check for conditions like:
inactive departments
inactive / terminated employees
period you are interested in for comparing the salary
Your queries looks like it will work. You can rewrite it to remove all the sub-queries (that will require additional table/index scans) and just use analytic queries:
SELECT employee_id,
last_name,
salary
FROM (
SELECT employee_id,
last_name,
salary,
AVG( salary ) OVER () AS avg_salary,
COUNT( CASE WHEN LOWER( last_name ) LIKE '%u%' THEN 1 END )
OVER ( PARTITION BY department_id ) AS num_last_name_with_u
FROM employees
)
WHERE salary > avg_salary
AND num_last_name_with_u > 0;
db<>fiddle
My first Question are you getting the expected result ?
Let me break down your Query
SELECT department_id FROM employees WHERE LOWER(last_name)
Here you are selecting the department so it retrieve the department id, what is the need of selecting department Id when all you need employee_id with last name contains u so change it to employee_id instead of department_id
select avg(salary) over (partition by department_id order by employee_id)
So using partition by you must get the avg salary per department
SELECT employee_id,last_name,salary
FROM
employees
WHERE salary>(SELECT AVG(salary) OVER (PARTITION BY department_id)
FROM
employees )
AND employee_id IN
( SELECT employee_id
FROM
employees
WHERE LOWER(last_name) LIKE '%u%')
Let me know if you have any issues running it, any corrections to Query is appreciated
What is wrong with this SQL query?
SELECT
department_id, MAX(AVG(SALARY))
FROM
EMPLOYEES
GROUP BY
department_id;
It shows not a single-group group function
2 Aggregate functions in one Query can not be done, you should use a Subquery to achieve your result.
I've not possibility to test it right now so no guarantees on this query but you may get an idea.
select max (avg_salary)
from (select department_id, avg(SALARY) AS avg_salary
from EMPLOYEES
group by department_id);
The inner query selects deparment_id and average salary.
Avarage salary is selected using the alias avg_salary using the AS statement.
The outer query selects the maximum of avg_salary-
That's maybe not a complete solution to your problem and as I said, not tested so no guarantees, but you should have an idea now how to start. ;-)
You cant have more than one aggregate functions in one query. try this one
select dept, max(average) over (partition by dept)
from (SELECT department_id dept,
(AVG(SALary) OVER (PARTITION BY department_id)) average
FROM employees);
Alternative 1, double GROUP BY:
SELECT department_id, AVG(SALARY)
FROM EMPLOYEES
GROUP BY department_id
HAVING AVG(SALARY) = (select max(avg_sal)
from (select avg(salary) as avg_sal
from EMPLOYEES
group by department_id))
Will return both department_id's if there's a tie!
Alternative 2, use a cte (common table expression):
with
(
SELECT department_id, AVG(SALARY) as avg_sal
FROM EMPLOYEES
GROUP BY department_id
) as cte
select department_id, avg_sal
from cte
where avg_sal = (select max(avg_sal) from cte)
This too will return both department_id's if there's a tie!
O community, do you know how I could select the department_ID, and lowest salary of the department with the highest average salary? Or how to eliminate the'ORA-00934: group function not allowed here' issue? Would I need to use two subqueries?
So far, this is what I've come up with, trying to get the department_ID of the highest paid department:
SELECT department_ID, MIN(salary
FROM employees
WHERE department_ID = (SELECT department_ID
FROM employees WHERE salary = MAX(salary));
Thank you, your assistance is greatly appreciated.
I can't test this, but it should work:
;WITH DepartmentsSalary AS
(
SELECT department_ID, AVG(Salary) AvgSalary, MIN(Salary) MinSalary
FROM employees
GROUP BY department_ID
)
SELECT department_ID, MinSalary
FROM ( SELECT department_ID, AvgSalary, MAX(AvgSalary) OVER() MaxSalary, MinSalary
FROM DepartmentsSalary) D
WHERE MaxSalary = AvgSalary
You can use join (then you have just one sub query)
select e1.department_ID, min(e1.salary)
from employees e1
join (
select avg_query.department_ID, max(avg_query.avg_value)
from (
select department_ID, avg(salary) as avg_value
from employees
group by department_ID
) avg_query
) e2 on e2.department_ID = e1.department_ID
;
First sub-query returned average salary for all departments
Next sub-query based on first sub-query returned highest average
salary and related department_ID
Main query returned min salary for department_ID with highest average
salary