3rd highest salary in each department - sql-server

I have the following tables: https://pastebin.com/Js0Sm69S (CREATE and INSERT statements).
I would like to find the third-highest salary in each department if there is such.
I was able to achieve this:
Using the following query:
SELECT *,
DENSE_RANK() OVER
(PARTITION BY DepartmentId ORDER BY Salary DESC) AS DRank
FROM Employees
I am not sure if DENSE_RANK() is the best ranking function to use here. Maybe not, because WHERE DRank=3 may return more than one result (but we can say TOP(1)). What do you think about this? Now how to display the third-highest salary in each department if there is such?

Try this
Select EmployeeID,FirstName,DepartmentID,Salary
From (
Select *
,RN = Row_Number() over (Partition By DepartmentID Order By Salary)
,Cnt = sum(1) over (Partition By DepartmentID)
From Employees
) A
Where RN = case when Cnt<3 then Cnt else 3 end

You're almost there, but you can achieve this with ROW_NUMBER, instead of DENSE_RANK. I think following query should help.
WITH cte AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY DepartmentId ORDER BY Salary DESC) AS DRank
FROM Employees
)
SELECT *
FROM cte
WHERE DRank= 3

Related

Find the third-highest salary in each department

I am trying to find the third-highest salary in each department if there is such.
SELECT DepartmentID
FROM Employees
GROUP BY DepartmentID
This is what I can do.
I looked at similar posts but not sure I understand how to do it with my table.
You can use row_number function to assign a order of salary, then get the 3rd one:
SELECT s.DepartmentID, s.Salary
FROM (
SELECT DepartmentID, Salary, ROW_NUMBER() OVER(PARTITION BY DepartmentID ORDER BY Salary DESC) AS salary_rank
FROM Employees) s
WHERE s.salary_rank=3
SELECT e.first_name,
e.last_name,
d.department_name,
salary,
ROW_NUMBER() OVER ( PARTITION BY d.id ORDER BY salary DESC ) AS
salary_rank
FROM department d
JOIN employee e ON d.id = e.department_id
ORDER BY department_name;

Query table and Select latest 2 rows (in SQL Server)

I have a table that logs all updates made to an application. I want to query the table and return the last update by [Timestamp] and the update before that for a different value [ITEM]. I'm struggling to figure out how to get what i need. I'm returning more than one record for each ID and don't want that.
;WITH cte AS
(
SELECT
ID,
LAG(ITEM) OVER (PARTITION BY ID ORDER BY timestamp DESC) AS ITEM,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY timestamp DESC) RN
FROM
MyLoggingTable
WHERE
accountid = 1234
)
SELECT
cte.ID,
dl.ITEM,
DL.timestamp
FROM
cte
JOIN
MyLoggingTable DL ON cte.ID = DL.ID
WHERE
rn = 1
AND cte.ID IN ('id here | Sub select :( ..')
Is ID unique? Because if it is, your code shouldn't return duplicates. If it isn't, you will get duplicates because you are joining back to the MyLoggingTable which isn't needed. You should just move those columns (dl.Item & dl.timestamp) into the cte and return them from the cte like you did cte.ID.
I removed the LAG since you didn't return that column in your final query.
;WITH cte AS
(
SELECT
ID,
ITEM,
[timestamp],
--LAG(ITEM) OVER (PARTITION BY ID ORDER BY timestamp DESC) AS ITEM,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY timestamp DESC) RN
FROM
MyLoggingTable
WHERE
accountid = 1234
)
SELECT
cte.ID,
cte.ITEM,
cte.timestamp
FROM
cte
WHERE
rn = 1
AND cte.ID IN ('id here | Sub select :( ..')
Note, if you wanted the second to the last item, as you stated in your comments, make rn=2

Latest Customer Donation And Amount

I have two tables:
Customer which has an Id column representing the customer Id.
CustomerDonation that contains CustomerId (FK), Amount and DatePayed
I'd like have all the customers together with their latest donation and the amount of that donation.
I am receiving duplicate values on my query so I will not paste it here.
You could also use the WITH TIES option
Select Top 1 With Ties *
From YourTable
Order By Row_Number() over (Partition By CustomerId Order By DatePayed Desc)
WITH
SortedDonation AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY DatePayed DESC) AS SeqID,
*
FROM
CustomerDonation
)
SELECT
*
FROM
Customer
LEFT JOIN
SortedDonation
ON SortedDonation.CustomerId = Customer.Id
AND SortedDonation.SeqId = 1
If the same customer can make multiple donations with the same DatePayed, then this will arbitrarily pick just one of them.
If you add additional fields to the ORDER BY you can deterministically pick which one you want.
Or, if you want all of them use DENSE_RANK() instead of ROW_NUMBER()
Use Row_Number() Analytic function .
Select * from (
Select customerId,Amount,DatePayed, row_number() over (partition by CustomerId order by DatePayed desc) as rowN)
as tab where rowN = 1
You only need the CustomerDonation table for this. You can join with the Customer table if you want other information of the customer.
WITH cte AS (
SELECT
CustomerId
, MAX(DatePayed) AS LastDate
FROM
CustomerDonation
)
SELECT
cd.CustomerId
, cd.Amount
, cd.DatePayed
FROM
CustomerDonation cd
JOIN cte ON cd.CustomerId = cte.CustomerId
AND cd.DatePayed = cte.LastDate

QUALIFY-Like Function in SQL Server

My table has 2 columns: Emp_Name and Emp_NR.
It can happen that one Emp_Name value has 2 different Emp_NR values.
I would like to create a SELECT statement that gets only a single value of Emp_Name and Emp_NR
The statement should be something like in Teradata:
SELECT
Emp_Name,
Emp_NR
FROM Table
QUALIFY Row_Number OVER (PARTITION BY Emp_Name ORDER BY Emp__NR DESC) = 1
In addition to that, I would like to get the highest Emp_NR that is assigned to a specific Emp_Name.
Another way is to use ORDER BY combined with TOP 1 WITH TIES:
SELECT TOP 1 WITH TIES Emp_Name, Emp_NR
FROM Table
ORDER BY ROW_NUMBER() OVER (PARTITION BY Emp_Name ORDER BY Emp_NR DESC);
Performance may be slight worse than the solution using subquery.
You did everything right.
SELECT Emp_NR, Emp_Name
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY Emp_Name ORDER BY Emp_NR DESC) AS RN, Emp_Name, Emp_NR
FROM YourTable
) AS T
WHERE T.RN = 1;
This is correct syntax.

sql server - selecting top x rows by group

I have a problem that I've wasted way too much time playing with. It can be simplified to something like:
Platform: SQL Server
you have a table with age and zipcode
list the top 5 oldest people in each zipcode
I can see how to do it with cursors, but is there a way with top and group by to achieve this?
All inputs appreciated!
SELECT *
FROM (
SELECT *
,ROW_NUMBER() OVER (PARTITION BY zipcode ORDER BY Age DESC) rn
FROM TableName
)A
WHERE RN <= 5
You need to use ROW_NUMBER analytic function
SELECT *
FROM
( SELECT name,
age,
zipcode,
ROW_NUMBER() OVER
( PARTITION by zipcode
order by age desc)
as seq
) T
Where T.seq <=5

Resources