SQL combining two queries inside the same table without a subquery - sql

A table, Employee has columns EmployeeID, Salary.
How do I find the EmployeeIDs which have a salary greater than the average salary?
Using Subqueries:
SELECT EmployeeID
FROM Employee
WHERE Salary > (SELECT AVG(Salary)
FROM Employee);
Is it possible using joins?
Is any other method possible?

It seems really silly to me to write it this way, but here it is without any subqueries:
SELECT a.EmployeeID
FROM Employee a
CROSS JOIN Employee b
GROUP BY a.EmployeeID, a.Salary
HAVING a.Salary > AVG(b.Salary)

SELECT EmployeeID
FROM Employee, (SELECT AVG(Salary) avg_savary
FROM Employee) sal
WHERE Salary > sal.avg_savary;
Move the average salary calculation to FROM to calculate it once. BTW most of moder DB can optimize your query to calculate it once.

SELECT * from
(SELECT EmployeeID,salary AVG(salary) OVER() avg_salary FROM Employee)
WHERE Salary >avg_salary

SELECT e.EmployeeID FROM Employee e
JOIN
(
SELECT avg(Salary) as Salary FROM Employee
) e1
ON e.Salary > e1.Salary

Declare #AverageSalary as Money
Select #AverageSalary = AVG(Salary) From Employee
Select * from Employee Where Salary > #AverageSalary

Related

I want to select the employees from my employees able (I named it ANGAJATI) who has the salary > avg(salary) of their department

In the employees table I have id_departament
And I can`t figure it out how to extract the avg salary for every employee
Use window functions if your RDBMS supports them:
select *
from (
select e.*, avg(salary) over(partition by id_department) avg_salary_dept
from employee e
) t
where salary > avg_salary_dept
Alternatively, you can join the table with an aggregate query that computes the average salary per department:
select e.*
from employee e
inner join (
select id_department, avg(salary) avg_salary_dept
from employee
group by id_department
) a on e.id_department = a.id_department and e.salary > a.avg_salary_dept

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)

Subquery with multiple averages

I've been struggling with a problem that goes something like this, "Find all employees with a salary greater than the average salary of their department." My sql subquery lumps all the departments salaries together to make one average salary but I need a way to get the each individual department's average salary.
My sql statement looks like this.
SELECT EmployeeName
FROM dbo.EMP
WHERE Salary > (
SELECT AVG(Salary)
FROM dbo.EMP
)
GROUP BY DeptNo
here is quick variant:
select EmployeeName
from
dbo.EMP as a
inner join
(
SELECT DeptNo, AVG(Salary) as avgSalary
FROM dbo.EMP
GROUP BY DeptNo
) as b
on (a.DeptNo=b.DeptNo and a.Salary > b.avgSalary)
You can just finish off that subquery to make it a correlated subquery:
SELECT EmployeeName
FROM dbo.EMP as t1
WHERE Salary > (
SELECT AVG(Salary)
FROM dbo.EMP
WHERE dbo.EMP.DeptNo = t1.DeptNo
)
Alternatively you could use Window Functions:
SELECT
EmployeeName,
CASE WHEN Salary > AVG(Salary) OVER (PARTITION BY DeptNo) Then 'X' END as [HigherThanAverage]
FROM dbo.EMP
That will give you all employees and an indicator if their salary is higher than their department's average, which you could filter out later on. I figured I'd stick this in here since it gives you some options as the scale of your query grows.

Oracle - How to return average from a subquery?

I need to select employees having salary bigger than the average salary grouped by departments.
SELECT * FROM employees
WHERE salary > (SELECT AVG(salary), department_id FROM employees GROUP BY department_id)
It's failing because It returns me 2 columns.
I have tried with this query:
SELECT * FROM employees
HAVING salary > AVG(salary)
GROUP BY (department_id)
Now i am getting error message: ORA-00979: not a GROUP BY expression
The simplest cross-database approach would be to use a JOIN:
SELECT employees.*
FROM employees
JOIN ( SELECT department_id, AVG(salary) avgSalary
FROM employees
GROUP BY department_id) departmentSalaries
ON employees.department_id = departmentSalaries.department_id
AND employees.salary > departmentSalaries.avgSalary
The most efficient approach for Oracle would be to use an analytic function (aka window function):
SELECT * FROM (
SELECT e.*, AVG(e.salary) OVER ( PARTITION BY e.department_id ) as avgSalary
FROM employees e) t
WHERE salary > avgSalary

How can I get all employees with a salary less than the average salary?

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