Perform SELECT statement inside of SELECT Statement? - sql

I'm using Emp, Dept... databases. I would like to get the Name, Salary, Deptno and Average salary in the department of those employees who earn more than the average of their department. So here is what I'm trying to do:
SELECT e.Ename, e.Sal, e.Deptno
, (
SELECT AVG(Sal)
FROM Emp b
WHERE b.Deptno = e.Deptno
GROUP BY Deptno
) AS 'Average Salary'
FROM Emp e
WHERE e.Sal > (
SELECT AVG(b.Sal)
FROM Emp b
WHERE b.Deptno = e.Deptno
GROUP BY Deptno
);
And I can't use AVG(Sal), because it will give the average salary of the employee, and not the department where he works.

You would use a correlated subquery:
SELECT e.Ename, e.Sal, e.Deptno,
(SELECT AVG(e2.Sal)
FROM Emp e2
WHERE e2.Deptno = e.Deptno
) AS [Average Salary]
FROM Emp e
WHERE e.Sal > (SELECT AVG(e2.Sal)
FROM Emp e2
WHERE e2.Deptno = e.Deptno
);
But in actuality, you would just use a window function:
select e.*
from (select e.*, avg(sal) over (partition by deptno) as avg_sal
from emp e
) e
where sal > avg_sal;

Just get rid of the group by.
SELECT e.Ename, e.Sal, e.Deptno, (SELECT AVG(Sal)
FROM Emp b
WHERE b.Deptno = e.Deptno
) AS 'Average Salary'
FROM Emp e
WHERE e.Sal > (SELECT AVG(b.Sal)
FROM Emp b
WHERE b.Deptno = e.Deptno
);

If you join to the subquery, you will not need to repeat it.
SELECT e.Ename, e.Sal, e.Deptno, dAvgs.avgSal AS 'Average Salary'
FROM Emp AS e
INNER JOIN (
SELECT Deptno, AVG(b.Sal) AS avgSal
FROM Emp b
GROUP BY Deptno
) AS dAvgs
ON e.Deptno = dAvgs.Deptno AND e.Sal > dAvgs.avgSal
;

Related

Select the manager name with the most employees

We have a table emp with columns empno, ename, job, mgr, hiredate, sal, comm, deptno
I have tried
SELECT m.ename, COUNT(e.empno) FROM emp e
INNER JOIN emp m ON e.empno = m.empno
GROUP BY m.ename HAVING COUNT(e.empno) = GREATEST(COUNT(e.empno));
My output is the names of the managers each with the value 1
How do we output the name of the manager with the most employees?
The following works:
SELECT COUNT(e.empno), m.ename FROM emp e
INNER JOIN emp m ON e.mgr = m.empno
GROUP BY m.ename HAVING GREATEST(COUNT(e.ename)) = COUNT(e.ename)
LIMIT 1;
First, fix the ON clause. Second, Use ORDER BY and LIMIT if you want one row:
SELECT m.ename, COUNT(e.empno)
FROM emp e INNER JOIN
emp m
ON m.empno = e.mgr
GROUP BY m.ename
ORDER BY COUNT(e.empno) DESC
LIMIT 1;
Your HAVING clause does not filter anything because any (non-NULL) value is equal to itself.

How to write the SQL query for summarizing salary

Q. I want a update query for updating the sum of salaries of employees departmentwise in the dept
table
DEPT table : columns :: DEPTNO DNAME LOC SUM_SAL
10 'CC' 'BLR'
20 'ADMIN' 'DEL'
30 'HR' 'CAL'
update a
set sum_sal = b.sum_sal
from dept a,
(select deptno, sum(sal) sum_sal from emp group by deptno ) b
where a.deptno = b.deptno;
But it is not working
update a
set sum_sal = b.sum_sal
from dept a,
(select deptno, sum(sal) sum_sal from emp group by deptno ) b
where a.deptno = b.deptno;
update a
set sum_sal = b.sum_sal
from dept a,
(select deptno, sum(sal) sum_sal from emp group by deptno ) b
where a.deptno = b.deptno;
from dept a,
ERROR at line 3: ORA-00933: SQL command not properly ended
Oracle doesn't support a from clause in an update.
Just use a correlated subquery:
update dept
set sum_sal = (select sum(e.sal) from emp e where e.deptno = dept.deptno);
One option would be using with..as select ... clause within an update statement :
update dept d
set d.sum_sal =
( with e as (
select *
from emp
)
select sum(e.sal)
from e
where e.deptno = d.deptno
group by e.deptno
);
Demo
You can use MERGE statement as it is a good candidate for MERGE
Try the following query:
MERGE INTO DEPT D
USING
(
SELECT
E.DEPTNO,
SUM(E.SAL) SUM_SAL
FROM
EMP E
GROUP BY
E.DEPTNO
) E
ON ( E.DEPTNO = DEPT.DEPTNO )
WHEN MATCHED THEN
UPDATE SET D.SUM_SAL = E.SUM_SAL;
Cheers!!

Select departments with or without employees. Also select employees if assigned

I want to select DNAME, DEPTNO from all departments with or without employees assigned. And if a department does have any employees I want to get their ENAME, EMPNO.
I tried this: select e.empno,e.name,d.deptno,d.dname from emp e full join dept d
on e.deptno=d.deptno
But it didn't work.
Table structure:
DEPT
DEPTNO DNAME
EMP
EMPNO ENAME DEPTNO
Use OUTER JOIN(LEFT/RIGHT)
SELECT D.DEPTNO,
D.DNAME,
E.EMPNO,
E.ENAME
FROM DEPT D
LEFT OUTER JOIN EMP E
ON D.DEPTNO = E.DEPTNO
When the Department does not have any employee then NULL will be displayed in E.EMPNO and E.ENAME column
SELECT
DEPT.DEPTNO,DEPT.DNAME,EMP.EMPNO,EMP.NAME
FROM
DEPT LEFT OUTER JOIN
EMP ON DEPT.DEPTNO = EMP.DEPTNO

Using WHERE clause on a joined table

I am trying to find the best way to filter out rows by using conditions based on a joined table. As an example I am joining the employees and salary grade table based on the salary grade for each employee. Then I want to show only the employees that have the same grade as a certain employee (Blake). I used the following code:
SELECT e.ename, e.sal, sg.grade
FROM emp e JOIN salgrade sg
ON(e.sal BETWEEN sg.losal AND sg.hisal)
WHERE sg.grade = (SELECT sg.grade FROM emp e JOIN salgrade sg ON(e.sal BETWEEN sg.losal AND sg.hisal) WHERE e.ename = 'BLAKE')
ORDER BY e.sal DESC
Is there a more optimal way to write the query?
Here is one method that uses window functions:
SELECT es.*
FROM (SELECT e.ename, e.sal, sg.grade,
MAX(CASE WHEN e.ename = 'BLAKE' THEN sg.grade END) OVER () as blake_grade
FROM emp e JOIN
salgrade sg
ON e.sal BETWEEN sg.losal AND sg.hisal
) es
WHERE grade = blake_grade
ORDER BY e.sal DESC;
I wouldn't use a join in the select in the WHERE clause; rather, I would use an inner scalar subquery to pick up BLAKE's salary and then an outer scalar subquery to pick up his salgrade. Otherwise very similar to your query:
select e.ename, e.sal, s.grade
from emp e inner join salgrade s on e.sal between s.losal and s.hisal
where s.grade = ( select grade
from salgrade
where (select sal from emp where ename = 'BLAKE')
between losal and hisal
)
order by sal desc
;
Using the same idea, you could do away with the first join as well (by returning the losal and hisal for BLAKE as well as his salgrade), but perhaps that is taking it too far.
If this is just about not having to write the same code twice, you can use a WITH clause:
WITH emps_and_sals AS
(
SELECT e.ename, e.sal, sg.grade
FROM emp e
JOIN salgrade sg ON e.sal BETWEEN sg.losal AND sg.hisal
)
SELECT *
FROM emps_and_sals
WHERE grade = (SELECT grade FROM emps_and_sals WHERE ename = 'BLAKE')
ORDER BY sal DESC;

Display empno,ename,sal,deptno,dname,location, Display highest paid employee of every department

How to display highest paid employee of every department WITH empno,ename,sal,deptno,dname,location....
select e.empno,e.ename, max(e.Sal),d.Deptno
from DEPTNO d
inner join emp e
on d.Deptno = e.DEPTNO
group by d.DEPTNO;
Employee table:
Deptno table:
Using the row_number() analytic function makes this easy:
select e.empno,
e.ename,
e.sal,
d.deptno,
d.dname,
d.loc
from Deptno d
join (select e.*,
row_number() over (
partition by e.deptno
order by sal desc) as rn
from Employee e) e
on e.deptno = d.deptno
and e.rn = 1
In this case, the row_number() function will return a value of 1 for the highest paid employee of every department, so that's why the join condition includes e.rn = 1 to limit the results to those employees.
Try this :
select empno,ename,deptno,sal from
(
select e.empno,e.ename,d.Deptno,sal,max(sal) over (partition by d.deptno order by null) max_sal from Employee e,Deptno d where e.deptno = d.deptno
)where sal=max_sal
here max_sal return max salary with partition by department