Using AVG() function between two tables - sql

I have two tables, and I need to determine the company that offers the highest average salary for any position. My tables are as follows:
employer
eID (primary key), eName, location
position
eID (primary key), pName (primary key), salary)
The code I wrote determines all avg salaries that are higher than one, but I need to find only the highest average salary over all
Here is my code so far:
SQL> select eName
2 from Employer E inner join position P on E.eID = P.eID
3 where salary > (select avg(salary) from position);
This outputs all salaries that are higher than the lowest average, but I need only the highest average. I tried using avg(salary) > (select avg(salary) from position) but I received the error that group function is not allowed.
Any help or suggestions would be greatly appreciated!

Use:
SELECT x.eid,
x.ename,
x.avg_salary
FROM (SELECT e.eid,
e.ename,
AVG(p.salary) AS avg_salary,
ROWNUM
FROM EMPLOYER e
JOIN POSTITION p ON p.eid = e.eid
GROUP BY e.eid, e.ename
ORDER BY avg_salary) x
WHERE x.rownum = 1
Oracle 9i+:
SELECT x.eid,
x.ename,
x.avg_salary
FROM (SELECT e.eid,
e.ename,
AVG(p.salary) AS avg_salary,
ROW_NUMBER() OVER(PARTITION BY e.eid
ORDER BY AVG(p.salary) DESC) AS rank
FROM EMPLOYER e
JOIN POSTITION p ON p.eid = e.eid
GROUP BY e.eid, e.ename) x
WHERE x.rank = 1
Previously, because the question was tagged "mysql":
SELECT e.eid,
e.ename,
AVG(p.salary) AS avg_salary
FROM EMPLOYER e
JOIN POSTITION p ON p.eid = e.eid
GROUP BY e.eid, e.ename
ORDER BY avg_salary
LIMIT 1

select a.eid,
a.ename,
b.avg_salary
FROM EMPLOYER a
JOIN POSTITION b ON a.eid = b.eid
WHERE b.avg_salary =(SELECT max(x.avg_salary)
FROM (SELECT e.eid,
e.ename,
AVG(p.salary) AS avg_salary,
FROM EMPLOYER e
JOIN POSTITION p ON p.eid = e.eid
GROUP BY e.eid, e.ename) x
) y

Related

Highest paid employee + average salary of the departments

An employee belongs to a department (foreign key = D_ID). An employee has a SSN (primary key), name, salary and D_ID.
A department has multiple employees (primary key = ID)
I want a query that returns Department name| Name of Highest Paid Employee of that department | His Salary | Average salary of employees working in the same department.
I know how to select the first part:
SELECT
D.name, E.name, E.salary
FROM
Employee E, Department D
WHERE
salary IN (SELECT MAX(E.salary)
FROM Employee E
GROUP BY E.D_ID)
AND E.D_ID = D.ID
I know also how to select the last part:
SELECT AVG(E.salary)
FROM Employee E
GROUP BY E.D_ID
How do I put these together in a single query?
You can use window functions for that:
select department_name, employee_name, salary, avg_dept_salary
from (
select e.name as employee_name,
d.name as department_name,
e.salary,
max(e.salary) over (partition by d.id) as max_dept_salary,
avg(e.salary) over (partition by d.id) as avg_dept_salary
from Employee E
join Department D on e.d_id = d.id
) t
where salary = max_dept_salary
order by department_name;
The above is standard ANSI SQL and runs on all modern DBMS.
I would do something like this:
SELECT d.name
, e.name
, e.salary
, n.avg_salary
FROM Department d
JOIN ( SELECT m.d_id
, MAX(m.salary) AS max_salary
, AVG(m.salary) AS avg_salary
FROM Employee m
GROUP BY m.d_id
) n
ON n.d_id = d.id
JOIN Employee E
ON e.d_id = d.id
AND e.salary = n.max_salary

Need to retrieve columns from 3 tables

There are three tables: dept, emp, sal . You can find their structure and data in the images.
I need to extract the list of employees who have location as pune and have max salary in their department. Since there are five departments, the final output will contain five rows and columns of emp_id, dept, dept_id, salary.
I've tried...
select e.emp_id, dept,e.dept_id, max(sal) as 'highest salary'
from sal s,emp e,dept d
where e.emp_id = s.emp_id and d.dept_id = e.dept_id and loc ='Pune'
group by e.emp_id,e.dept_id,dept
order by e.dept_id
I would use apply :
select t.emp_id, d.dept, d.dept_id, t.sal
from dept d
cross apply ( select top 1 e.emp_id, s.sal as sal
from emp e
inner join sal s on s.emp_id = e.emp_id
where d.dept_id = e.dept_id and e.loc = 'Pune'
order by s.sal desc
) t;
Unless you have window functions (that depends on whether you're using MySQL, Oracle, SQLite, etc) you will need to do it in two steps.
Find the highest salary per department (for employees in 'Pune'), then another set of joins to find out who those people are.
SELECT
dep.dept,
dep.dept_id,
emp.emp_id,
sal.sal
FROM
emp
INNER JOIN
sal
ON sal.emp_id = emp.emp_id
INNER JOIN
(
SELECT
emp.dept_id,
MAX(sal.sal) AS max_sal
FROM
emp
INNER JOIN
sal
ON sal.emp_id = emp.emp_id
WHERE
emp.loc = 'Pune'
)
dep_sal
ON dep_sal.dept_id = emp.dept_id
AND dep_sal.max_sal = sal.sal
INNER JOIN
dep
ON dep.dept_id = emp.dept_id
WHERE
emp.loc = 'Pune'
ORDER BY
dep.dept,
emp.emp_id
EDIT: With SQL Server 2008 on-wards it's a bit easier...
WITH
emp_sal_ranked AS
(
SELECT
emp.dept_id,
emp.emp_id,
sal.sal,
RANK(sal.sal) OVER (PARTITION BY emp.dept_id
ORDER BY sal.sal
)
AS rank_sal
FROM
emp
INNER JOIN
sal
ON sal.emp_id = emp.emp_id
WHERE
emp.loc = 'Pune'
)
SELECT
dep.dept,
dep.dept_id,
emp_sal_ranked.emp_id,
emp_sal_ranked.sal
FROM
emp_sal_ranked
INNER JOIN
dept
ON dept.dept_id = emp_sal_ranked.dept_id
WHERE
emp_sal_ranked.rank_sal = 1
ORDER BY
dep.dept,
emp_sal_ranked.emp_id

Employees with largest salary in department

I found a couple of SQL tasks on Hacker News today, however I am stuck on solving the second task in Postgres, which I'll describe here:
You have the following, simple table structure:
List the employees who have the biggest salary in their respective departments.
I set up an SQL Fiddle here for you to play with. It should return Terry Robinson, Laura White. Along with their names it should have their salary and department name.
Furthermore, I'd be curious to know of a query which would return Terry Robinsons (maximum salary from the Sales department) and Laura White (maximum salary in the Marketing department) and an empty row for the IT department, with null as the employee; explicitly stating that there are no employees (thus nobody with the highest salary) in that department.
Return one employee with the highest salary per dept.
Use DISTINCT ON for a much simpler and faster query that does all you are asking for:
SELECT DISTINCT ON (d.id)
d.id AS department_id, d.name AS department
,e.id AS employee_id, e.name AS employee, e.salary
FROM departments d
LEFT JOIN employees e ON e.department_id = d.id
ORDER BY d.id, e.salary DESC;
->SQLfiddle (for Postgres).
Also note the LEFT [OUTER] JOIN that keeps departments with no employees in the result.
This picks only one employee per department. If there are multiple sharing the highest salary, you can add more ORDER BY items to pick one in particular. Else, an arbitrary one is picked from peers.
If there are no employees, the department is still listed, with NULL values for employee columns.
You can simply add any columns you need in the SELECT list.
Find a detailed explanation, links and a benchmark for the technique in this related answer:
Select first row in each GROUP BY group?
Aside: It is an anti-pattern to use non-descriptive column names like name or id. Should be employee_id, employee etc.
Return all employees with the highest salary per dept.
Use the window function rank() (like #Scotch already posted, just simpler and faster):
SELECT d.name AS department, e.employee, e.salary
FROM departments d
LEFT JOIN (
SELECT name AS employee, salary, department_id
,rank() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rnk
FROM employees e
) e ON e.department_id = d.department_id AND e.rnk = 1;
Same result as with the above query with your example (which has no ties), just a bit slower.
This is with reference to your fiddle:
SELECT * -- or whatever is your columns list.
FROM employees e JOIN departments d ON e.Department_ID = d.id
WHERE (e.Department_ID, e.Salary) IN (SELECT Department_ID, MAX(Salary)
FROM employees
GROUP BY Department_ID)
EDIT :
As mentioned in a comment below, if you want to see the IT department also, with all NULL for the employee records, you can use the RIGHT JOIN and put the filter condition in the joining clause itself as follows:
SELECT e.name, e.salary, d.name -- or whatever is your columns list.
FROM employees e RIGHT JOIN departments d ON e.Department_ID = d.id
AND (e.Department_ID, e.Salary) IN (SELECT Department_ID, MAX(Salary)
FROM employees
GROUP BY Department_ID)
This is basically what you want. Rank() Over
SELECT ename ,
departments.name
FROM ( SELECT ename ,
dname
FROM ( SELECT employees.name as ename ,
departments.name as dname ,
rank() over (
PARTITION BY employees.department_id
ORDER BY employees.salary DESC
)
FROM Employees
JOIN Departments on employees.department_id = departments.id
) t
WHERE rank = 1
) s
RIGHT JOIN departments on s.dname = departments.name
Good old classic sql:
select e1.name, e1.salary, e1.department_id
from employees e1
where e1.salary=
(select maxsalary=max(e.salary) --, e. department_id
from employees e
where e.department_id = e1.department_id
group by e.department_id
)
Table1 is emp - empno, ename, sal, deptno
Table2 is dept - deptno, dname.
Query could be (includes ties & runs on 11.2g):
select e1.empno, e1.ename, e1.sal, e1.deptno as department
from emp e1
where e1.sal in
(SELECT max(sal) from emp e, dept d where e.deptno = d.deptno group by d.dname)
order by e1.deptno asc;
SELECT
e.first_name, d.department_name, e.salary
FROM
employees e
JOIN
departments d
ON
(e.department_id = d.department_id)
WHERE
e.first_name
IN
(SELECT TOP 2
first_name
FROM
employees
WHERE
department_id = d.department_id);
`select d.Name, e.Name, e.Salary from Employees e, Departments d,
(select DepartmentId as DeptId, max(Salary) as Salary
from Employees e
group by DepartmentId) m
where m.Salary = e.Salary
and m.DeptId = e.DepartmentId
and e.DepartmentId = d.DepartmentId`
The max salary of each department is computed in inner query using GROUP BY. And then select employees who satisfy those constraints.
Assuming Postgres
Return highest salary with employee details, assuming table name emp having employees department with dept_id
select e1.* from emp e1 inner join (select max(sal) avg_sal,dept_id from emp group by dept_id) as e2 on e1.dept_id=e2.dept_id and e1.sal=e2.avg_sal
Returns one or more people for each department with the highest salary:
SELECT result.Name Department, Employee2.Name Employee, result.salary Salary
FROM ( SELECT dept.name, dept.department_id, max(Employee1.salary) salary
FROM Departments dept
JOIN Employees Employee1 ON Employee1.department_id = dept.department_id
GROUP BY dept.name, dept.department_id ) result
JOIN Employees Employee2 ON Employee2.department_id = result.department_id
WHERE Employee2.salary = result.salary
SQL query:
select d.name,e.name,e.salary
from employees e, depts d
where e.dept_id = d.id
and (d.id,e.salary) in
(select dept_id,max(salary) from employees group by dept_id);
Take look at this solution
SELECT
MAX(E.SALARY),
E.NAME,
D.NAME as Department
FROM employees E
INNER JOIN DEPARTMENTS D ON D.ID = E.DEPARTMENT_ID
GROUP BY D.NAME

Using MAX aggregate between two tables

I have two tables, employer and position:
Employer
eID
eName
Position
eID
salary
I need to match my eID between the two tables, determine what the max salary is, and print only the eName. Any suggestions as to how I can do this? I have tried multiple ways, but nothing seems to work.
I am not sure where to put in the max(salary) function:
select eName
from employer, position
where employer.eID = position.eID
To get the name(s) of the people with the highest salary...
Using a JOIN:
SELECT e.name
FROM EMPLOYER e
JOIN POSITION x ON x.eid = e.eid
JOIN (SELECT MAX(salary) AS max_salary
FROM POSITION) y ON y.max_salary = x.salary
Using a subquery:
SELECT e.name
FROM EMPLOYER e
JOIN POSITION p ON p.eid = e.eid
WHERE p.salary = (SELECT MAX(salary)
FROM POSITION)
select e.ename,p.salary
from employer e ,position p
where p.salary=(select max(salary) from position)
and e.eid=p.eid
Join the tables, sort, and get the first one:
select top 1 e.eName, p.salary
from Employer e
inner join Position p on p.eID = e.eID
order by p.salary desc
(This returns the salary also, but you can of course remove it if you really don't want it.)

SQL Query List Those that are higher than average

I have 2 tables in the following format:
employee (employeeID, EmployeeName, DepartmentID)
departments (DepartmentID, DeptName)
How many employees there are who work in each of the departments that have more employees than the average number of employees in a department.
im looking to the results in the following format:
Dept Name | Num of Emp
engineering | 10
science | 15
SELECT deptName, cnt
FROM (
SELECT departmentID, COUNT(*) AS cnt
FROM employee
GROUP BY
departmentID
HAVING COUNT(*) >=
(
SELECT AVG(cnt)
FROM (
SELECT COUNT(*) AS cnt
FROM employee
GROUP BY
departmentID
)
)
) e
JOIN departments d
ON d.departmentID = e.departmentID
In Oracle, you can use analytic functions which are more elegant:
SELECT DeptName, cnt
FROM (
SELECT q.*, AVG(cnt) OVER() AS acnt
FROM (
SELECT departmentID, COUNT(*) AS cnt
FROM employee
GROUP BY
departmentID
) q
) e
JOIN departments d
ON d.departmentID = e.departmentID
WHERE cnt >= acnt
since an employee can be in only one department, the average number of employees is just the total # of employees over the total number of departments. So how about:
SELECT dept.name, COUNT(emp.id) AS employeeCount
FROM emp INNER JOIN dept ON emp.deptId = dept.id
GROUP BY dept.name
HAVING (COUNT(emp.id) >
(SELECT COUNT(*) FROM emp) /
(SELECT COUNT(*) FROM dept))