I have 2 working subqueries that checks what are the min and max SUMS in all departments (DEPTNO). Table EMP and DEPT has DEPTNO cells
(SELECT min(sum(e.SAL)) FROM EMP e GROUP by e.DEPTNO);
(SELECT max(sum(e.SAL)) FROM EMP e GROUP by e.DEPTNO);
How to check what is the DEPTNO for min- and max- subquery?
My code is with ERROR:
SELECT d.DEPTNO
FROM DEPT d
WHERE sum(e.SAL) = (SELECT max(sum(e.SAL)) FROM EMP e GROUP by d.DEPTNO);
If you want the department withe the max sum, you can use rownum or row_number():
select ed.*
from (select e.deptno, sum(e.sal) as sums,
row_number() over (order by sum(e.sal) desc) as seqnum
from emp e
group by e.deptno
) ed
where seqnum = 1;
In Oracle 12g+, this can also be written as:
select e.deptno, sum(e.sal) as sums,
row_number() over (order by sum(e.sal) desc) as seqnum
from emp e
group by e.deptno
order by sum(e.sal) desc
fetch first 1 row only;
This is one way to do it using cte
with salsums as (select deptno, sum(sal) salsum from emp group by deptno)
, maxandmin as (select max(salsum) maxsal, min(salsum) minsal from salsums)
select deptno
from salsums cross join maxandmin
where salsum = maxsal or salsum = minsal
Ok, this is harder than it looks at first. This is how I ended up getting it to work but I like #vkp's answer more.
with sums as
(
SELECT DEPTNO, SUM(SAL) AS SSAL
FROM EMP
GROUP BY DEPTNO
), mm as
(
SELECT DEPTNO, SSAL, MIN(SSAL) OVER () as MIN_SSAL, MAX(SSAL) OVER () as MAX_SSAL
FROM SUMS
)
SELECT 'MIN', DEPTNO, SSAL FROM mm WHERE SSAL=MIN_SSAL
UNION ALL
SELECT 'MAX', DEPTNO, SSAL FROM mm WHERE SSAL=MAX_SSAL
http://sqlfiddle.com/#!6/410c8/8
Related
Have to find the most experienced employee.
select empno, ename,
from emp
where max(months_between(SYSDATE,hiredate))
My software is giving errors on sysdate, so could anyone please verify if this is correct.
Use NOT EXISTS:
select e.empno, e.ename
from emp e
where not exists (select 1 from emp where hiredate < e.hiredate)
or:
select empno, ename
from emp
where hiredate = (select min(hiredate) from emp)
or modify the WHERE clause of your query like this:
select empno, ename
from emp
where months_between(SYSDATE, hiredate) = (
select max(months_between(SYSDATE, hiredate))
from emp
)
You can use:
select e.*
from emp e
order by hiredate asc
fetch first 1 row only;
If you want duplicates, then add with ties.
The above works in Oracle 12C+. In earlier versions, you need a subquery:
select e.*
from (select e.*,
row_number() over (order by hiredate) as seqnum
from emp e
) e
where seqnum = 1;
I have the following ORACLE query where I attempt to find the department with the highest average salary. I would like to use in-line view (i.e. retain the b dataset) for this implementation, but struggle to get the right part at the WHERE and GROUP BY components. I know the below GROUP BY and WHERE (which is non-existant) is wrong. But how do i correct them?
select a.deptno from emp a,
(select max(avg_sal) max_avg_sal from (select
avg(sal) avg_sal from emp group by deptno) ) b
group by a.deptno, b.max_avg_sal
having avg(a.sal) = b.max_avg_sal
Expected Result
deptno
10
Emp Structure
deptno staff sal
10 A 1000
10 B 1500
11 C 1100
12 D 1000
12 E 900
12 F 1000
Is this what you want?
select e.*
from (select e.*, avg(e.salary) over (partition by e.deptno) as avg_salary
from emp e
) e
order by avg_salary desc
fetch first 1 row only;
fetch first is available in Oracle 12c+. You can do similar things with an additional subquery in earlier versions.
You can use subquery
select deptno from tablename
group by deptno
having avg(sal)= (select max(asal) from (select avg(sal) as asal from tablename group by deptdno)A)
The straight-forward way is:
select deptno
from emp
group by deptno
order by avg(salary) desc
fetch first row with ties;
FETCH FIRST is available as of Oracle 12c.
In Oracle 11g we could use this instead:
select deptno
from
(
select deptno, avg(salary) as avg_salary, max(avg(salary)) over () as max_avg_salary
from emp
group by deptno
)
where avg_salary = max_avg_salary;
But you want an inline view, another word for a derived table (a subquery in the from clause). That looks way more clumsy. One example without FETCH FIRST and without window functions:
with d as
(
select deptno, avg(salary) as avg_salary
from emp
group by deptno
)
, dmax as
(
select max(avg_salary) as max_avg_salary
from d
)
select d.*
from d
join dmax on dmax.max_avg_salary = d.avg_salary;
I find this very obfuscated and don't recommend it at all. You can do the same without WITH clauses of course. Then it is even less readable.
I don't know why you'd want to write it this way, but if you really want only inline views and no windowing clauses, you can write it this way:
select b.deptno
from (SELECT deptno, avg(sal) avgsal from emp group by deptno ) b
cross join (SELECT max(avgsal) maxavgsal FROM (SELECT avg(sal) avgsal FROM emp group by deptno )) c
where b.avgsal = c.maxavgsal;
This the same thing, if you don't like CROSS JOIN for some reason:
select b.deptno
from (SELECT deptno, avg(sal) avgsal from emp group by deptno ) b
inner join ( SELECT max(avgsal) maxavgsal FROM
( SELECT avg(sal) avgsal FROM emp group by deptno ) ) c
on b.avgsal = c.maxavgsal;
BREAK ON DEPTNO SKIP 1
compute sum of sal on deptno
SELECT deptno, empno, ename,sal FROM
(SELECT deptno, empno, ename, sal FROM emp )
WHERE EXISTS (SELECT deptno FROM dept) order by 1,2 , sal desc ;
How can I get two highest sal from emp, and what is wrong with my code?
If you want all rows with the two highest distinct salaries in each department, then use dense_rank() as follows:
select deptno, empno, ename, sal
from (select e.*,
dense_rank() over (partition by deptno, order by sal desc) as seqnum
from emp e
) e
where seqnum <= 2
order by deptno, sal desc;
It looks like the question will be deleted, but it might as well have a correct answer.
It is not entirely clear what you want. In the title you say "two highest salary", but in the comment you mention something about a sum.
The following will show the two highest salaries. If there are multiple "highest" salaries, all will be shown
select deptno, empno, ename, sal
from (
SELECT deptno, empno, ename, sal,
dense_rank() over (order by sal desc) as rnk
FROM emp
)
where rnk <= 2
order by sal desc;
To get this per department, you can use this:
select deptno, dept_salary
from (
select deptno, dept_salary,
dense_rank() over (order by dept_salary desc) as rnk
from (
SELECT deptno, sum(sal) as dept_salary
FROM emp
group by deptno
) t1
) t2
where rnk <= 2
order by dept_salary desc
Simple query actually:
SELECT deptno, empno, ename,sal FROM emp eb
WHERE (deptno, empno) IN
(SELECT depno, empno FROM
(SELECT deptno, empno FROM emp ei
WHERE ei.deptno = eb.deptno
ORDER BY ei.sal DESC
) WHERE rownum <= 2
);
The last WHERE rownum <=2 differ from SQL to SQL, in Mysql you would need LIMIT 2, in MSSQL Server, you would need to do SELECT TOP 2, in Oracle WHERE rownum <= 2. Depends on the engine you use.
I'm working on one SQL query.
Table name: employees.
I want to get the MAX and MIN sal with their employee names in SQL.
I know how to do with either MAX or MIN. But how can we do it both in one query?
I need a single row output like below:
e1.name AS MaxName, MAX(e1.sal) AS MaxSalary, e2.name AS MinName, MIN(e2.sal) AS MinSalary
In a single select:
SELECT MIN( salary ) AS MinSalary,
MIN( name ) KEEP ( DENSE_RANK FIRST ORDER BY salary ASC ) AS MinName,
MAX( Salary ) AS MaxSalary,
MAX( name ) KEEP ( DENSE_RANK LAST ORDER BY salary ASC ) AS MaxName
FROM Employees;
Two ways:
Using Analytic function:
SQL> SELECT MIN(ename) KEEP (DENSE_RANK FIRST ORDER BY sal) min_name,
2 MIN(sal) AS min_sal,
3 MAX(ename) KEEP (DENSE_RANK LAST ORDER BY sal) AS max_name,
4 MAX(sal) AS max_sal
5 FROM emp;
MIN_NAME MIN_SAL MAX_NAME MAX_SAL
---------- ---------- ---------- ----------
SMITH 800 KING 5000
Using an In-line view:
SQL> WITH DATA AS
2 ( SELECT MIN(sal) min_sal, MAX(sal) max_sal FROM emp
3 )
4 SELECT
5 (SELECT e.ename FROM DATA t, emp e WHERE e.sal = t.min_sal AND ROWNUM =1
6 ) min_name,
7 (SELECT t.min_sal FROM DATA t, emp e WHERE e.sal = t.min_sal AND ROWNUM =1
8 ) min_sal,
9 (SELECT e.ename FROM DATA t, emp e WHERE e.sal = t.max_sal AND ROWNUM =1
10 ) max_name,
11 (SELECT t.max_sal FROM DATA t, emp e WHERE e.sal = t.max_sal AND ROWNUM =1
12 ) max_sal
13 FROM dual;
MIN_NAME MIN_SAL MAX_NAME MAX_SAL
---------- ---------- ---------- ----------
SMITH 800 KING 5000
Assuming that you are using Oracle, try this. Here first we are getting rownumber in ascending and descending order and then doing a cross join.
with employee(id,name,sal) as
(select 1,'a',1000 from dual union all
select 3,'c',1500 from dual union all
select 2,'b',2000 from dual) --temp table to recreate the scenario
, enew as(
select e.*,row_number() over (order by sal) as salasc,row_number() over (order by sal desc) as saldesc from employee e
) --temp table to find the rownumber in ascending and descending order
--original query
select * from (select id as minsalid,name as minsalempname,sal as minsal from enew
where salasc=1)
cross join
(select id as maxsalemp,name as maxsalempname,sal as maxsal from enew
where saldesc=1)
The following solution works for MySQL, which was one of the tags you originally had when you posted your question.
You can perform a CROSS JOIN of the employees table against itself to find the max name/salary with a query which finds the min name/salary.
SELECT e1.name AS MaxName, MAX(e1.sal) AS MaxSalary,
e2.name AS MinName, MIN(e2.sal) AS MinSalary
FROM employees e1 CROSS JOIN employees e2
Click the link below for a running demo. I actually include the name/salary pairs, though you can remove the names if you don't want them there.
SQLFiddle
Try something like:
select max(sal), min(sal), employee_id
from employees
group by employee_id;
After that you can join it to get the name. Maybe you can group by name and id too.
Task :
List Name and salary of highest and lowest paid employee from each department.
You can perform the queries for highest and lowest separately also.
Tried Query :
SELECT dept.deptno,
dname,
minsal,
maxsal
FROM dept,
(SELECT deptno,
Max (sal) MAXSAL
FROM emp
GROUP BY deptno) MAXSALARY,
(SELECT deptno,
Min (sal)MINSAL
FROM emp
GROUP BY deptno) MINSALARY
WHERE MAXSALARY.deptno = dept.deptno
AND MINSALARY.deptno = dept.deptno;
Result
The Result is correct.
Question)
Is there any other way to simplify the query?
You can use ORACLE's window functions like row_number combined with conditional aggregation using CASE EXPRESSION :
SELECT t.ename,t.dname,
MAX(CASE WHEN t.low_ind = 1 then t.salary END),
MAX(CASE WHEN t.high_ind = 1 then t.salary END)
FROM (SELECT e.name as ename,d.name as dname,e.salary,
ROW_NUMBER() OVER(PARTITION BY d.name ORDER BY e.salary ASC) as low_ind,
ROW_NUMBER() OVER(PARTITION BY d.name ORDER BY e.salary DESC) as high_ind
FROM emp e
INNER JOIN dept d
ON(d.deptno = e.deptno)) t
GROUP BY t.ename,t.dname
EDIT: if all you need is the min and max salary on each department then thats a simple group by clause :
SELECT d.deptno,d.dname,MIN(e.salary) as min_sal,MAX(e.salary) as max_sal
FROM dept d
INNER JOIN emp e
ON(d.deptno = e.deptno)
GROUP BY d.deptno,d.name
SELECT d.dname, Max(e.sal) MAXSAL, Min(e.sal) MINSAL
FROM dept d, emp e
WHERE d.deptno = e.deptno
group by d.dname