Top 2 Salary Grouped By Department - sql

Below is the table I am referring to.
I want to find ou the 2 Employees in each department with highest salary.

Further to the above answer, if there are ties (multiple employees sharing the same salary), you can use the following to bring them all through instead of just picking two at random (which is what the ROW_NUMBER clause will do)
SELECT *
FROM (
SELECT *, DENSE_RANK() OVER (PARTITION BY Dept ORDER BY Salary DESC) AS rn
FROM MyTable ) t
WHERE t.rn <= 2

Use ROW_NUMBER() to get the top salaries per Department, then select the first two records from each departmental partiton:
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Dept ORDER BY Salary DESC) AS rn
FROM MyTable ) t
WHERE t.rn <= 2

Related

Write SQL & Hive query to print the year in which the 2nd highest salary was paid for each country?

Write SQL and HIVE query to print the year in which the 2nd highest salary was paid for each country?.
Please provide query for the below table
country,salary,year
india,1000,2017
japan,2000,2017
germany,1500,2017
india,1250,2018
japan,500,2018
china,955,2017
japan,850,2019
china,1150,2018
india,1250,2019
something like:
select
t.*
from (
select
tbl.*,
row_number() over(partition by country order by salary desc) rn
from
tbl
) t
where
t.rn = 2
The big question is how you handle ties. Presumably, you mean the second highest distinct salary. In that case, you are specifically looking for the dense_rank() window function:
select t.*
from (select t.*,
dense_rank() over (partition by country order by salary desc) as seqnum
from t
) t
where t.seqnum = 2;
Now, the challenge with this is that it could return more than one row in the event of ties. If you specifically want one row, then:
select t.*
from (select t.*,
dense_rank() over (partition by country order by salary desc) as ranking,
row_number() over (partition by country, salary order by country) as seqnum
from t
) t
where t.ranking = 2 and seqnum = 1;

Row_number function using directly

Is there any direct way of using row_number() function? I want to find 2 nd highest salary
SELECT DISTINCT id
,salary
,depid
,ROW_NUMBER() OVER (
PARTITION BY depid ORDER BY salary DESC
) AS rownum
FROM emp
WHERE rownum = 2;
It gives an error, However the below code works fine.
SELECT *
FROM (
SELECT DISTINCT id
,salary
,depid
,ROW_NUMBER() OVER (
PARTITION BY depid ORDER BY salary DESC
) AS rownum
FROM emp
) AS t
WHERE t.rownum = 2;
Is any way of directly using the row_number() function as in the first option which is giving the error?
You can not use the alias name of the same query as the condition for the where clause. You also can not use windowed queries as a passing condition in the where clause.
Here is a detailed explanation Why no windowed functions in where clauses?. It is so you need another query outside the inner query and needs to write sub-query.
You can get the Nth highest salary in SQL Server from the below query.
SELECT TOP 1 salary
FROM (
SELECT DISTINCT TOP N salary
FROM <YourTableNameHere>
ORDER BY salary DESC
) AS TEMP
ORDER BY salary
This query will give you the second highest salary ? No
SELECT id
,salary
,depid
from emp
ORDER BY salary DESC
OFFSET 1 ROWS
FETCH FIRST 1 ROWS ONLY;
Well actually, it will give you the salary that is on the second position when you order the salary's from highest to lowest... So if the highest is 100 and the second highest is 100 then you will get 100 as a result. To conclude this will return a row on the second place depending on the order by clause...
This next query will give you the second highest salary :
SELECT max(id)
, salary
, max(depid)
from emp
group by salary
ORDER BY salary DESC
OFFSET 1 ROWS
FETCH FIRST 1 ROWS ONLY;
But be aware, in case you have two employees from two different departments with the same salary then it will return you the one with the higher id and it will return the higher department id which can be incorrect.
And finally this will give you one employee that has a second largest salary with correct data:
SELECT id
, salary
, depid
from emp
where id = (SELECT max(id)
from emp
group by depid, salary
ORDER BY salary DESC
OFFSET 1 ROWS
FETCH FIRST 1 ROWS ONLY);
First, you want dense_rank(), not row_number() if you want the second highest value -- ties might get in the way otherwise.
You can use an arithmetic trick:
SELECT TOP (1) WITH TIES id, salary, depid
FROM emp
ORDER BY ABS(DENSE_RANK() over (PARTITION BY depid ORDER BY salary DESC) - 2)
The "-2" is an arithmetic trick to put the "second" values highest.
That said, I would stick with the subquery because the intent in clearer.
You could use a variation on the trick that uses a TOP 1 WITH TIES in combination with an ORDER BY ROW_NUMBER
SELECT TOP 1 WITH TIES
id,
salary,
depid
FROM emp
ORDER BY IIF(2 = ROW_NUMBER() OVER (PARTITION BY depid ORDER BY salary DESC), 1, 2)
But this trick does have the disadvantage that you can't sort it by something else.
Well, not unless you wrap it in a sub-query and sort the outer query.
A test on rextester here
I prefer to use dense_rank() instead of row_number() function with CTE (common table expression) for the scenario you have mentioned. CTE is modern, easy to use and have many cool features like it is memory resident, it can be used for DUI operations, it make code easy to understand etc.
To find Nth highest salary, the CTE look like
;with findnthsalary
as
(
select empid, deptid, salary,
dense_rank() over(partition by deptid order by salary desc) salrank
from
Employee
)
select distinct id, deptid, salary
from findnthsalary
where salrank = N
I used dense_rank() because if you use row_number() it will produce the wrong result in case multiple employees have the same salary in the same department.

How to fetch rows with MAX condition involving multiple columns

I have an Employee table with the following columns:
Emp_Id
Emp_Name
ProjectName
ProjectStartDate
ProjectEndDate
Emp_ManagerId
Every employee can work in multiple projects at the same time.
I want the fetch all the records in the table for each employee with max ProjectStartDate. If an employee has multiple records with same ProjectStartDate then the record with max ProjectEndDate should be returned.
All dates in MM/DD/YYYY
You can use ROW_NUMBER() in a subquery to rank the records of each employee by descending project start and end date, and then filter on the top record of each employee in the outer query:
SELECT *
FROM (
SELECT
e.*,
ROW_NUMBER() OVER(PARTITION BY Emp_Id ORDER BY ProjectStartDate DESC, ProjectEndDate DESC) rn
FROM Employee e
) x
WHERE rn = 1
You can use the ROW_NUMBER() function, as in:
select *
from (
select
*,
row_number() over(partition by emp_id
order by projectstartdate desc, projectenddate desc) as rn
from employee
) x
where rn = 1

Finding department having maximum number of employee

I have a table employee
id name dept
1 bucky shp
2 name shp
3 other mrk
How can i get the name of the department(s) having maximum number of employees ? ..
I need result
dept
--------
shp
SELECT cnt,deptno FROM (
SELECT rank() OVER (ORDER BY cnt desc) AS rnk,cnt,deptno from
(SELECT COUNT(*) cnt, DEPTNO FROM EMP
GROUP BY deptno))
WHERE rnk = 1;
Assuming you are using SQL Server and each record representing an employee. So you can use window function to get the result
WITH C AS (
SELECT RANK() OVER (ORDER BY dept) Rnk
,name
,dept
FROM table
)
SELECT TOP 1 dept FROM
(SELECT COUNT(Rnk) cnt, dept FROM C GROUP BY dept) t
ORDER BY cnt DESC
With common table expressions, count the number of rows per department, then find the biggest count, then use that to select the biggest department.
WITH depts(dept, size) AS (
SELECT dept, COUNT(*) FROM employee GROUP BY dept
), biggest(size) AS (
SELECT MAX(size) FROM depts
)
SELECT dept FROM depts, biggest WHERE depts.size = biggest.size
Based on one of the answer, Let me try to explain step by step
First of all we need to get the employee count department wise. So the firstly innermost query will run
select count(*) cnt, deptno from scott.emp group by deptno
This will give result as
Now out of this we have to get the one which is having max. employee i.e. department 30.
Also please note there are chances that 2 departments have same number of employees
The second level of query is
select rank() over (order by cnt desc) as rnk,cnt,deptno from
(
select count(*) cnt, deptno from scott.emp group by deptno
)
Now we have assigned ranking to each department
Now to select rank 1 out of it. we have a simplest outer query
select * from
(
select rank() over (order by cnt desc) as rnk,cnt,deptno from
(
select count(*) cnt, deptno from scott.emp group by deptno
)
)
where rnk=1
So we have the final result where we got the department which has the maximum employees. If we want the minimum one we have to include the department table as there are chances there is a department which has no employees which will not get listed in this table
You can ignore the scott in scott.emp as that is the table owner.
The above SQL can be practised at Practise SQL online

SQL command for finding the second highest salary

HI,
Can u tell me the syntax of the SQL command which gives as output the second highest salary from a range of salaries stored in the employee table.
A description of the SQL commnd will be welcomed...
Please help!!!
select min(salary) from
(select top 2 salary from SalariesTable order by salary desc)
as ax
This should work:
select * from (
select t.*, dense_rank() over (order by salary desc) rnk from employee t
) a
where rnk = 2;
This returns the second highest salary.
dense_rank() over is a window function, and it gives you the rank of a specific row within the specified set. It is standard SQL, as defined in SQL:2003.
Window functions are awesome in general, they simplyfy lots of difficult queries.
Slightly different solution:
This is identical except that returns the highest salary when there is a tie for number 1:
select * from (
select t.*, row_number() over (order by salary desc) rnk from employee t
) a
where rnk = 2;
Updated: Changed rank to dense_rank and added second solution. Thanks, IanC!
with tempTable as(
select top 2 max(salary) as MaxSalary from employee order by salary desc
) select top 1 MaxSalary from tempTable
description:
select the top 2 maximum salaries
order them by desc order ( so the 2nd highest salary is now at the top)
select the top 1 from that
another approach:
select top 1 MaxSalary from (
select top 2 max(salary) as MaxSalary from employee order by salary desc
)
Here's some sample code, with proof of concept:
declare #t table (
Salary int
)
insert into #t values (100)
insert into #t values (900)
insert into #t values (900)
insert into #t values (400)
insert into #t values (300)
insert into #t values (200)
;WITH tbl AS (
select t.Salary, DENSE_RANK() OVER (order by t.Salary DESC) AS Rnk
from #t AS t
)
SELECT *
FROM tbl
WHERE Rnk = 2
DENSE_RANK is mandatory (change to RANK & you'll see).
You'll also see why any SELECT TOP 2 queries won't work (without a DISTINCT anyway).
You don't specify the actual SQL product you're using, and the query language varies among products. However, something like this should get you started:
SELECT salary FROM employees E1
WHERE 1 = (SELECT COUNT(*) FROM employee E2 WHERE E2.salary > E1.salary)
(thanks to fredt for the correction).
Alternatively (and faster in terms of performance) would be
SELECT TOP 2 salary FROM employees ORDER BY salary DESC
and then skipping the first returned row.
An alternative (tested):
select Min(Salary) from (
select distinct TOP (2) salary from employees order by salary DESC) AS T
This will work on any platform, is clean, and caters for the possibility of multiple tied #1 salaries.
Select top 1 * from employee where empID in (select top 2 (empID) from employee order by salary DESC) ORDER BY salary ASC
Explanation:
select top 2 (empID) from employee order by salary DESC would give the two records for which Salary is top and then the whole query would sort it these two records in ASCENDING order and then list out the one with the lowest salary among the two.
EX. let the salaries of employees be 100, 99, 98,,50.
Query 1 would return the emp ID of the persons with sal 100 and 99
Whole query would return all data related to the person having salary 99.
SELECT Salary,EmpName
FROM
(
SELECT Salary,EmpName,ROW_NUMBER() OVER(ORDER BY Salary) As Rank
FROM EMPLOYEE
) A
WHERE A.Rank=n;
where n is the number of highest salary u r requesting the table.
Ucan also use DenseRank() function in place of ROW_NUMBER().
Thanks,
Suresh
An easier way..
select MAX(salary) as SecondMax from test where salary !=(select MAX(salary) from test)