This question already has answers here:
SELECT every employee that has a higher salary than the AVERAGE of his department
(8 answers)
Closed 8 years ago.
i have a table called employees which i have name, department_id and salary in it. I want to find the employees whose salary is greater than the average of their department and see their names, department_id, salary and the average salary of their department. I have written this code but it does not work. How can we fix this? Thanks in advance.
SELECT name, department_id, salary, avg(salary)
FROM employees
GROUP BY name, department_id, salary
HAVING salary > (select avg(salary) from employees group by department_id)
I have updated my code as you said like:
SELECT department_id, salary, avg(salary), count(*)
FROM employees e
GROUP BY department_id, salary
HAVING salary > (select avg(salary) from employees e2 where e2.department_id=e.department_id)
But when i run this i get this result:
You can see that salary and the averages are the same and there are 2 department 80's, i need 1 of all the existing departments. How can we fix this. I am using the Oracle database if that's any important. Thanks.
Your code is quite close. But, instead of a group by in the subquery, it needs to be correlated to the outer query. And, you don't need an outer aggregation, just a where clause:
SELECT name, department_id, salary
FROM employees e
WHERE salary > (select avg(salary) from employees e2 where e2.department_id = e.department_id);
However, you are presumably learning Oracle. You can also write this query using analytic functions:
select e.*
from (select e.*, avg(salary) over (partition by department) as avgsalary
from employees e
) e
where e.salary > e.avgsalary;
Although this is probably a bit advanced for what you are learning, I encourage you to understand both queries.
Related
SELECT
job_id, emp_name, salary, AVG(SALARY) AS AVERAGE_SALARY
FROM
employees
GROUP BY
emp_name, department_id;
I've tried this but this doesn't seem to work.
table: https://i.stack.imgur.com/3jB6x.png
output
: https://i.stack.imgur.com/q7R5T.png
my output: https://i.stack.imgur.com/EfxcZ.png
You seem to want a window average:
select
job_id, emp_name, salary,
avg(salary) over(partition by department_id) as avg_dpt_salary
from employees
This gives you one row per employee, along with the average salary of all employees of the same department.
You're not grouping by salary, yet you want the average. In this case the non-averaged salary column will return a value from a single record if it even works at all.
There's a couple ways to interpret what you're asking for.
Perhaps you want a report of each employee, but you want the average for the entire department. I believe you're grouping by emp_name because you want each employee's name in the output. You could do that this way in MariaDB:
select
job_id, emp_name, salary, (
select avg(salary) from employees b where b.department_id = a.department_id
) as avg_salary
from
employees a;
The other way to interpret what you're asking is if the same employee has multiple entries in the database and you only want one entry per employee name. The problem there, is what value are you expecting for the non-averaged salary? I've excluded that in the next example since it doesn't make sense to me.
select
emp_name, department_id, avg(salary) as avg_salary
from
employees a;
group by
emp_name, department_id;
I'm not certain either of these actually generate the data you're looking for, so please give me feedback on which of these is closer to what you're looking for and I'll edit the answer if necessary.
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.
how to display the name, salary and the avg(salary) for all the employees whose salary is greater than the company avg(salary).
I have tried the following query:
Select last_name, salary
From employees
Where salary >(select avg(salary) from employees);
This gives the names of those employees who are getting higher salary than the company avg(salary). But I want to display the avg(salary) in the select list aswell.
Join the employee table to a query that produces the average:
select last_name, salary, avg_salary
from employees
join (select avg(salary) avg_salary from employees) x
on salary > avg_salary
This query will work on all databases.
Assuming sql server, here is but one of many ways to accomplish this:
DECLARE #AverageSalary MONEY
SELECT #AverageSalary=AVG(SALARY) FROM EMPLOYEES
Select last_name, salary, #AverageSalary From employees Where salary > #AverageSalary
I can get the count of employees and avg salary but when I try to get the the addition select of listing the number of employees paid below the average it fails.
select count(employee_id),avg(salary)
from employees
Where salary < avg(salary);
select count(*), (select avg(salary) from employees)
from employees
where salary < (select avg(salary) from employees);
The problem is that AVG is an aggregation function. SQL is not smart enough to figure out how to mix aggregated results within the rows. The traditional way is to use a join:
select count(*), avg(e.salary),
sum(case when e.salary < const.AvgSalary then 1 else 0 end) as NumBelowAverage
from employees e cross join
(select avg(salary) as AvgSalary from employees) as const
select TotalNumberOfEmployees,
AverageSalary,
count(e.employee_id) NumberOfEmployeesBelowAverageSalary
from (
select count(employee_id) TotalNumberOfEmployees,
avg(salary) AverageSalary
from employees
) preagg
left join employees e on e.salary < preagg.AverageSalary
group by TotalNumberOfEmployees,
AverageSalary
Note: I used a LEFT join so if you had 3 equal employees, it would show 0 instead of no results (nobody below below average).
It isn't clear which columns you want in your result set, which makes it difficult to answer your question. Making the question clear improves the quality of the answers.
You seem to want 3 facts:
Number of employees.
Average salary.
Number of employees earning less than the average salary.
And you show a query which does the job for the first two facts:
SELECT COUNT(*) AS NumberOfEmployees,
AVG(Salary) AS AverageSalary
FROM Employees
What's the difference between COUNT(*) and COUNT(Employee_ID)? The difference is that the latter only counts the rows where there is a non-NULL value in the Employee_ID column. A good optimizer will recognize that Employee_ID is a primary key and contains no NULL values, and the query will be the same. But COUNT(*) is more conventional and less reliant on the optimizer.
The other statistic can be generated as a simple value in the select-list via a sub-query:
SELECT COUNT(*) AS NumberOfEmployees,
AVG(Salary) AS AverageSalary,
(SELECT COUNT(*)
FROM Employees
WHERE Salary < (SELECT AVG(Salary) FROM Employees)
) AS NumberOfEmployeesPaidSubAverageWages
FROM Employees
Under many circumstances, it would not be appropriate to write the sub-query like that, but for the interpretation of the specified query, it is fine.
select * from <table name> where salary < (select avg(<salary column name) from <table name>);
Example:
select * from EMPLOYEE where sal < (select avg(emp_sal) from EMPLOYEE);
SELECT e.ename,e.deptno,e.sal,d.avg
FROM emp e,(SELECT deptno, avg(sal) avg
FROM emp
GROUP BY deptno) d
WHERE e.deptno=d.deptno
AND
e.sal < d.avg
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