How to get two highest salary in emp table? - sql

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.

Related

Displaying the statistics from emp and dept table in a single query

I have two table emp and dept
Emp table has following fields:
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
Dept table has following fields:
DEPTNO DNAME LOC
Rules are:-
a. total employees in each department
b. highest salary of each department
c. least salary of each department.
d. No. of emps with highest salary in each department.
e. No. of emps with least salary in each department.
f. employee name/id with highest salary in each department.
g. employee name/id with least salary in each department.
h. Names of all employees belonging to eachdepartment.
o/p is as follows:
for data of emp and dept table refer this link
http://sqlfiddle.com/#!4/1bc2b8
Thanks in advance..
You can use analytical function and GROUP BY as follows:
SELECT D.deptno,
COUNT(1) TOTAL_EMPS,
MAX(sal) AS MAX_SAL,
MIN(sal) AS MIN_SAL,
SUM(CASE WHEN SAL = MINSAL THEN 1 END) EMP_MIN_SAL,
SUM(CASE WHEN SAL = MAXSAL THEN 1 END) EMP_MAX_SAL,
MAX(CASE WHEN RNMIN = 1 THEN E.empno END) EMP_WITH_MIN_sAL,
MAX(CASE WHEN RNMAX = 1 THEN E.empno END) EMP_WITH_MAX_sAL,
LISTAGG(E.ename, ',') WITHIN GROUP (ORDER BY E.EMPNO) ALL_EMPS
FROM dept D JOIN (SELECT T.*,
MIN(SAL) OVER (PARTITION BY deptno) MINSAL,
MAX(SAL) OVER (PARTITION BY deptno) MAXSAL,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY SAL) RNMIN,
ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY SAL DESC) RNMAX
FROM emp T) E
ON E.deptno = D.deptno
GROUP BY D.deptno;
SQLFiddle demo
Using aggregate functions and group by....without using any analytical functions
Select deptno,
COUNT(*) tot_emps,
MAX(sal) max_sal,
MIN(sal) min_sal,
COUNT(sal) KEEP (DENSE_RANK FIRST ORDER BY sal desc) emp_max_salary,
COUNT(sal) KEEP (DENSE_RANK FIRST ORDER BY sal asc) emp_min_salary,
MAX(empno || '-' || ename) KEEP (DENSE_RANK FIRST ORDER BY sal DESC) emp_with_max_salary,
MIN(empno || '-' || ename) KEEP (DENSE_RANK FIRST ORDER BY sal ASC)emp_with_min_salary,
LISTAGG(ename,',') WITHIN GROUP (ORDER BY ename) emp_list
FROM emp
GROUP BY deptno
ORDER BY deptno;
SQLFiddle_Demo

Querying the Oracle SCOTT Schema – What salary is the closest to the average?

Using the SCOTT Schema. I’m trying to get the person who has a salary closest to the average.
SELECT sal
FROM ( SELECT sal
FROM emp
ORDER BY ABS ( (SELECT AVG (SAL) FROM EMP) - sal))
WHERE ROWNUM = 1;
Can the solution above be improved?
You are using a non-standard Oracle feature. Data in subqueries doesn't have an order originally, but in Oracle you can order it, so as to be able to apply the ROWNUM criteria later. ROWNUM is also Oracle specific.
Moreover there can be several employees with the same salary, where you are picking one of them arbitrarily rather than showing them all.
Here is how to select the employee(s) with the salary closest to the avarage in standard SQL:
select *
from emp
order by abs(sal - avg(sal) over())
fetch first row with ties;
This last line is available only as of Oracle 12c.
In older versions (that is Oracle 9i, 10i or 11g) you'd rank your rows instead:
select empno, ename, sal
from
(
select empno, ename, sal, rank() over (order by diff) as rnk
from
(
select emp.*, abs(sal - avg(sal) over()) as diff
from emp
) evaluated
) ranked
where rnk = 1;
Pulling employee data back with the subquery:
select empno, sal, SalDif
from
(
select empno, sal, abs(sal - (select avg(sal) from emp)) as SalDif
from emp
order by SalDif
)
where rownum = 1;
or as a CTE:
with CTE as
(
select empno, sal, abs(sal - (select avg(sal) from emp)) as SalDif
from emp
order by SalDif
)
select empno, sal, SalDif
from CTE
where rownum = 1;
with av as (select avg(sal) avgsal from scott.emp)
select emp.*, abs(emp.sal-av.avgsal) dist
from scott.emp, av
order by dist;
Above looks better for me but probably you can get it best with analytic function. Something like:
select * from (
select emp.*, abs(avg(emp.sal) over () - sal) diff
from scott.emp order by diff)
where rownum = 1;

SQL select rows without duplicates

Practicing some SQL, we have to get the name of the employees whose salary is the greatest of his department. But if in any department there were more than one employer with the greatest salary, we would not have to consider that department.
We got the first part but not the second one (because there are two employees with the same greatest salary (3,000) in the same department (20)).
This is what we did:
SQL> SELECT ename, sal, deptno FROM emp a
WHERE sal >= ALL (SELECT sal FROM emp WHERE deptno=a.deptno)
ORDER BY sal;
And this is what we got:
ENAME SAL DEPTNO
---------- ------- ------
BLAKE 2,850 30
FORD 3,000 20
SCOTT 3,000 20
KING 5,000 10
4 filas seleccionadas.
Any help will be useful, thank you!
SELECT ename, sal, deptno
FROM emp a
WHERE not exists (
SELECT *
FROM emp
WHERE deptno=a.deptno
and sal >= a.sal
and ename != a.ename)
ORDER BY sal;
with cte as
( SELECT ename, sal, deptno
, row_number() over (partition by deptno order by sal desc) as rn
FROM emp
)
select ename, sal, deptno from cte where rn = 1
except
select ename, sal, deptno from cte where rn = 2
order by sal
if this does not work in oracle - it used to be also tagged mssql
You can have what you need with some analytic functions:
select ename,
deptno,
sal
from (
select ename,
deptno,
sal,
row_number() over(partition by deptno order by sal desc) AS num,
count(1) over(partition by deptno, sal) AS count
from emp
)
where num = 1
and count = 1
The inner query orders by salary and counts the number of employees with the same salary in the same department; the outer one simply filters for employees with the maximum salary, where only one employee has that salary in the department.
With a different approach, simply modifying your query, you can try:
SELECT ename, sal, deptno FROM emp a
WHERE sal >= ALL (SELECT sal FROM emp WHERE deptno=a.deptno)
and (select count(1) from emp b where a.deptno = b.deptno and a.sal = b.sal) = 1
The first way gives better performance, with a single table scan, while the second one needs a nested query, thus being less efficient
try to use GROUP BY column_name`, it will show the record without duplicate.

Nth max salary in Oracle

To find out the Nth max sal in oracle i'm using below query
SELECT DISTINCE sal
FROM emp a
WHERE (
SELECT COUNT(DISTINCE sal)
FROM emp b
WHERE a.sal<=b.sal)=&n;
But According to me by using the above query it will take more time to execute if table size is big.
i'm trying to use the below query
SELECT sal
FROM (
SELECT DISTINCE sal
FROM emp
ORDER BY sal DESC )
WHERE rownum=3;
but not getting output.. any suggetions please .. Please share any link on how to optimise queries and decrease the time for a query to execute.
try this
select *
from
(
select
sal
,dense_rank() over (order by sal desc) ranking
from table
)
where ranking = 4 -- Replace 4 with any value of N
SELECT sal FROM (
SELECT sal, row_number() OVER (order by sal desc) AS rn FROM emp
)
WHERE rn = 3
Yes, it will take longer to execute if the table is big. But for "N-th row" queries the only way is to look through all the data and sort it. It will be definitely much faster if you have an index on sal.
SELECT *
FROM Employee Emp1
WHERE (N-1) = (
SELECT COUNT(DISTINCT(Emp2.Salary))
FROM Employee Emp2
WHERE Emp2.Salary > Emp1.Salary)
This will show the 3rd max salary from table employee.
If you want to find out the 5th or 6th (whatever you want) value then just change the where condition like this where rownum<=5" or "where rownum<=6 and so on...
select min(sal) from(select distinct(sal) from emp where rownum<=3 order by sal desc);
SELECT Min(sal)
FROM (SELECT DISTINCT sal
FROM emp
WHERE sal IS NOT NULL
ORDER BY sal DESC)
WHERE rownum <= n;
These queries will also work:
Workaround 1)
SELECT ename, sal
FROM Emp e1 WHERE n-1 = (SELECT COUNT(DISTINCT sal)
FROM Emp e2 WHERE e2.sal > e1.sal)
Workaround 2) using row_num function.
SELECT *
FROM (
SELECT e.*, ROW_NUMBER() OVER (ORDER BY sal DESC) rn FROM Emp e
) WHERE rn = n;
Workaround 3 ) using rownum pseudocolumn
Select MAX(SAL)
from (
Select *
from (
Select *
from EMP
order by SAL Desc
) where rownum <= n
)
The following solution works from 12c onwards:
Select min(sal) from emp where
Sal in ( select distinct (sal) from emp order by sal desc fetch first n rows only);
Replace n as per your requirement
We could write as below mentioned also.
select min(sal) from (select sal from emp where rownum=<&n order by sal desc);
In my case this Query is successfully executed (Oracle).
select salary from
(select salary, (dense_rank()
over (order by salary desc)) R
from employees)
where R='10' group by salary;
you can replace value '10' by any value of 'n'.
SELECT sal
FROM (
SELECT empno,
deptno, sal,
dense_rank( ) over ( partition by deptno order by sal desc) NRANK
FROM emp
)
WHERE NRANK = 4
SELECT *
FROM (
SELECT empno,
deptno, sal,
dense_rank( ) over ( order by sal desc) NRANK
FROM emp
)
WHERE NRANK = 4
you can replace the 2 with your desired number
select * from ( select distinct (sal),ROW_NUMBER() OVER (order by sal desc) rn from emp ) where rn=2
Refer following query for getting nth highest salary. By this way you get nth highest salary. If you want get nth lowest salary only you need to replace DESC by ASC in the query.
Now you try this you will get for sure:
SELECT DISTINCT sal
FROM emp a
WHERE (
SELECT COUNT(DISTINCT sal)
FROM emp b
WHERE a.sal<=b.sal)=&n;
For your information, if you want the nth least sal:
SELECT DISTINCT sal
FROM emp a
WHERE (
SELECT COUNT(DISTINCT sal)
FROM emp b
WHERE a.sal>=b.sal)=&n;
select min(sal) from (select distinct sal from employee order by sal DESC) where rownum<=N;
place the number whatever the highest sal you want to retrieve.
Try out following:
SELECT *
FROM
(SELECT rownum AS rn,
a.*
FROM
(WITH DATA AS -- creating dummy data
( SELECT 'MOHAN' AS NAME, 200 AS SALARY FROM DUAL
UNION ALL
SELECT 'AKSHAY' AS NAME, 500 AS SALARY FROM DUAL
UNION ALL
SELECT 'HARI' AS NAME, 300 AS SALARY FROM DUAL
UNION ALL
SELECT 'RAM' AS NAME, 400 AS SALARY FROM DUAL
)
SELECT D.* FROM DATA D ORDER BY SALARY DESC
) A
)
WHERE rn = 3; -- specify N'th highest here (In this case fetching 3'rd highest)
Cheers!
select * FROM (
select EmployeeID, Salary
, dense_rank() over (order by Salary DESC) ranking
from Employee
)
WHERE ranking = N;
dense_rank() is used for the salary has to be same.So it give the proper output instead of using rank().
SELECT TOP (1) Salary FROM
(
SELECT DISTINCT TOP (10) Salary FROM Employee ORDER BY Salary DESC
) AS Emp ORDER BY Salary
This is for 10th max salary, you can replace 10 with n.
This will also work :
with data as
(
select sal,rwid from (
select salary as sal,rowid as rwid from salary order by salary desc
)
where rownum < 5
)
select * from salary a
where rowid = (select min(rwid) from data)
select min(sal) from (select distinct(sal) from emp order by sal desc) where rownum <=&n;
Inner query select distinct(sal) from emp order by sal desc will give the below output as given below.
SAL
5000
3000
2975
2850
2450
1600
1500
1300
1250
1100
950
800
without distinct in the above query select sal from emp order by sal desc
output as given below.
SAL
5000
3000
3000
2975
2850
2450
1600
1500
1300
1250
1250
1100
950
800
outer query will give the 'N'th max sal (E.g) I have tried here for 4th Max sal and out put as given below.
MIN(SAL)
2850
Select min(salary) from (
select distinct(salary) from empdetails order by salary desc
) where rownum <=&rn
Just enter nth number which you want.
Try this:
SELECT min(sal) FROM (
SELECT sal FROM emp ORDER BY sal desc) WHERE ROWNUM <= 3; -- Replace 3 with any value of N
You can optimize the query using Dense_rank() function.
for Example :
select distinct salary from
( select salary ,dense_rank() over (order by salary desc) ranking
from Employee
)
where ranking = 6
Note: ranking 6 is the number of nth order.
select * from (select rownum as rownumber,emp1.* from (select * from emp order by sal desc) emp1) where rownumber = 3;
Try this one :
Select sal
From (Select rownum as rank, empno,ename,sal
From (Select *
From emp order by sal desc)
)
where rank=2;
Just add the number as rank which will give you nth highest salary.
SELECT MIN(Salary) salary
FROM (
SELECT DISTINCT Salary
FROM Employee
ORDER BY Salary DESC
)
WHERE ROWNUM <= n
ORDER BY salary ASC;
-- replace "n" from line number 7 with anything you want
select MIN(salary) from (select distinct salary from employees order by SALARY DESC) WHERE ROWNUM <= 3;
5th highest salary:
SELECT
*
FROM
emp a
WHERE
4 = (
SELECT
COUNT(DISTINCT b.sal)
FROM
emp b
WHERE
a.sal < b.sal
)
Replace 4 with any value of N.
There are three methods are there...
SELECT salary,first_name,rnk
FROM (SELECT salary,first_name,rank() over (order by salary desc nulls last) as rnk from emp) where rnk=3;
SELECT salary,first_name,rnk
FROM (SELECT salary,first_name,dense_rank() over (order by salary desc nulls last) as rnk from emp) where rnk=3;
select rnk,first_name,salary
from (select rownum as rnk ,first_name,salary
from (select first_name,salary
from emp order by salary desc nulls last)) where rnk=3

Combining columns

I am trying to do the following:
I have a table with ename, job, deptno, and sal. I am trying to initiate a query that returns the top earners of each department. I have done this with grouping and a subquery. However, I also want to display the average sal by deptno. So the following would be the result:
"ename" "dept" "sal" "average of dept"
sal 20 1000 500
kelly 30 2000 800
mika 40 3000 400
this might be impossible since the average does not associate with the other rows.
any suggestion would be appreciated. Thanks. I am using Oracle 10g to run my queries.
You could use analytic functions:
WITH RankedAndAveraged AS (
SELECT
ename,
dept,
sal,
RANK() OVER (PARTITION BY dept ORDER BY sal DESC) AS rnk,
AVG(sal) OVER (PARTITION BY dept) AS "average of dept"
FROM atable
)
SELECT
ename,
dept,
sal,
"average of dept"
FROM RankedAndAveraged
WHERE rnk = 1
This may return more than one employee per department if all of them have the same maximum value of sal. You can replace RANK() with ROW_NUMBER() if you only want one person per department (in which case you could also further extend ORDER BY by specifying additional sorting criteria to pick the top item, otherwise it will be picked randomly from among those with the maximum salary).
This should work. The only trick is that if you have several employees with the maximum salary in a department, it will show all of them.
SELECT t.ename, t.deptno, mx.sal as sal, mx.avg_sal as avg_sal
FROM tbl t,
(SELECT MAX(sal) AS sal, AVG(sal) AS avg_sal, deptno
FROM tbl
GROUP BY deptno) mx
WHERE t.deptno = mx.deptno AND t.sal = mx.sal
Not sure about Oracle, haven't used it in about 10 years, but something like this should be possible:
SELECT
ename, deptno, sal,
(SELECT AVG(T2.sal)
FROM tbl T2
WHERE T2.deptno = T.deptno
) AS average_of_dept
FROM tbl T
GROUP BY deptno
HAVING sal = MAX(sal)