Select the lowest n values from a group - sql-server

This is kind of like this questions:
T-SQL: How to use MIN
But I want it to return the lowest 4 values per group.
Thank you

Use a CTE with ROW_NUMBER:
WITH CTE AS(
SELECT T.*, RN=ROW_NUMBER()OVER(PARTITION BY Col1 Order By Col2 ASC)
FROM dbo.TableName T
)
SELECT * FROM CTE WHERE RN <= 4
Ranking Functions

SELECT
ID, SomeVal
FROM (
SELECT ID, SomeVal, row_number() over(PARTITION BY id ORDER BY SomeVal ASC) rn
FROM [TableName]
) T
WHERE rn <= 4
This gives you the lowest first 4 SomeVal values per ID.

Related

SQL Server : how to select last 3 rows in the table ordered by desc and then sort them asc?

I have a CTE with the last 3 rows of the table sorted by VersionNumber DESC. How I can sort them backwards in order to have the smallest VersionNumber from those 3 always to have display order 1?
There is no LIMIT in Microsoft SQL Server...
;WITH cte AS
(
SELECT
BSId, RevisedBSId, VersionNumber,
RANK() OVER (ORDER BY VersionNumber DESC) AS [DisplayOrder]
FROM
cte
),
sortedctethree AS
(
SELECT TOP 3
BSId, RevisedBSId, VersionNumber, DisplayOrder
FROM
sortedcte
)
SELECT *
FROM sortedctethree
Here DisplayOrder is wrong. If I use another CTE where I am trying to reorder, it is still wrong.
I need only 3 rows. But in the TOP 3 can be also one, or two rows in the result. I need in that case always have the DisplayOrder 1 for the smallest VersionNumber.
VersionNumber | DisplayOrder
--------------+--------------
2 | 2
1 | 1
How it is possible to achieve? Thanks a lot in advance.
try like below you dont need another cte
WITH cte AS
(
SELECT
BSId, RevisedBSId, VersionNumber,
rank() OVER (ORDER BY VersionNumber DESC) AS [DisplayOrder] from
table_name
)
SELECT BSId, RevisedBSId, VersionNumber , DisplayOrder FROM cte
where DisplayOrder<=3
order by DisplayOrder
You may try with the next approach. Number your data using DENSE_RANK() ordered by VersionNumber descending and then select first three rows and order them ascending:
;WITH cte AS (
SELECT
BSId,
RevisedBSId,
VersionNumber,
DENSE_RANK() OVER (ORDER BY VersionNumber DESC) AS [DisplayOrder]
FROM YourTable
)
SELECT
BSId,
RevisedBSId,
VersionNumber
FROM cte
WHERE [DisplayOrder] <= 3
ORDER BY VersionNumber ASC
If I understood correctly:
If you use ROW_NUMBER() with data ordered asc then the smallest value will have DisplayOrder = 1
;WITH cte AS
(
SELECT
BSId, RevisedBSId, VersionNumber,
ROW_NUMBER() OVER (ORDER BY VersionNumber ASC) AS [DisplayOrder]
FROM
cte
)
SELECT TOP 3
BSId, RevisedBSId, VersionNumber, DisplayOrder
FROM
sortedcte where [DisplayOrder] =1
If there are no partitions this is the simplest way, you dont need rank or row_number in your subquery or CTE
SELECT ROW_NUMBER() OVER (ORDER BY VersionNumber) as DisplayOrder, VesionNumber FROM (
SELECT TOP 3 VesionNumber from mytable
ORDER BY VesionNumber DESC
) A
ORDER BY VesionNumber ASC
Or using your approach
;WITH cte AS
(
SELECT
BSId, RevisedBSId, VersionNumber,
RANK() OVER (ORDER BY VersionNumber DESC) AS [SelectOrder],
RANK() OVER (ORDER BY VersionNumber ASC) AS [DisplayOrder],
FROM
cte
),
sortedctethree AS
(
SELECT TOP 3
BSId, RevisedBSId, VersionNumber, DisplayOrder, SelectOrder
FROM
sortedcte
ORDER BY SelectOrder
)
SELECT *
FROM sortedctethree
ORDER BY DisplayOrder

How to Find Ffifth Youngest Employee by DOB in Sql Server

What will be sql query for the fifth youngest employee?
Below query is wrong? please help
SELECT EmpID, EmpName, EMPDOB,
ORDER BY EMPDOB DESC
WHERE ROWNUMBER = 5
FROM dbo.EMP
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY EMPDOB ASC) AS rownumber,
EmpID, EmpName, EMPDOB
FROM EMPLOYEE
) AS foo
WHERE rownumber = 5
The following query will solve your problem:
SELECT TOP 1
T.EmpID
, T.EmpName
, T.EMPDOB
FROM (SELECT TOP 5 * FROM dbo.EMP ORDER BY EMPDOB DESC) AS T
Note: This solution assumes that EMPDOB is in a date or integer based format.

SQL LAG Days since last order

Hi I am trying to create a windowed query in SQL that shows me the days since last order for each customer.
It now shows me the days in between each order.
What do I need to change in my query to have it only show the days since the last and the previous order per customer? Now it shows it for every order the customer made.
Query:
SELECT klantnr,besteldatum,
DATEDIFF(DAY,LAG(besteldatum) OVER(PARTITION BY klantnr ORDER BY besteldatum),besteldatum) AS DaysSinceLastOrder
FROM bestelling
GROUP BY klantnr,besteldatum;
You can use row_number() to order the rows by besteldatum for each klantnr, and return the latest two using a derived table (subquery) or common table expression.
derived table version:
select klantnr, besteldatum, DaysSinceLastOrder
from (
select klantnr, besteldatum
, DaysSinceLastOrder = datediff(day,lag(besteldatum) over (partition by klantnr order by besteldatum),besteldatum)
, rn = row_number() over (partition by klantnr order by besteldatum desc)
from bestelling
group by klantnr, besteldatum
) t
where rn = 1
common table expression version:
;with cte as (
select klantnr, besteldatum
, DaysSinceLastOrder = datediff(day,lag(besteldatum) over (partition by klantnr order by besteldatum),besteldatum)
, rn = row_number() over (partition by klantnr order by besteldatum desc)
from bestelling
group by klantnr, besteldatum
)
select klantnr, besteldatum, DaysSinceLastOrder
from cte
where rn = 1
If you want one row per customer, rn = 1 is the proper filter. If you want n number of latest rows, use rn < n+1.

Order by a date but return a string in SQL

I'm trying to do the following (which obviously doesn't work because I am attempting to order by a column not in a group by clause), where TransDateString is a varchar column, defined as cast(datepart(m,TransDate) as varchar)+'-'+cast(datepart(yyyy,TransDate) as varchar) of the TransDate (date) column.
SELECT c.TransDateString
FROM #dataSet c
GROUP BY c.TransDateString
ORDER BY c.TransDate asc
What I'm trying to accomplish is order the results by date but return only the column as the formatted string.
Here's what the data and output I'm looking for would be:
TransDate | TransDateString
2005-01-01 | 01-2005
2012-15-05 | 05-2012
2003-22-10 | 10-2003
Results:
TransDateString
10-2003
01-2005
05-2012
;With cteRows As
(
SELECT c.TransDateString,
Row_Number() Over (Partition By c.TransDateString Order By c.TransDate) RowNum
FROM #dataSet c
)
Select TransDateString From cteRows Where RowNum = 1
Without CTE:
Select TransDateString From
(
Select c.TransDateString,
Row_Number() Over (Partition By c.TransDateString Order By c.TransDate) RowNum
FROM #dataSet c
) A
Where RowNum = 1

SQL Server 2008 R2 GROUP BY or OVER

I have this table:
ID COLOR TYPE DATE
-------------------------------
1 blue A 2012.02.05
2 white V 2010.10.23
3 white V 2014.03.05
4 black S 2013.02.14
I'd like to select only the ID, but in case of 2nd and 3rd rows I want to select the 3rd row because of its latest DATE value.
I have tried this query but it gives back all the two rows:
SELECT
ID, MAX(DATE) OVER(PARTITION BY COLOR, TYPE)
FROM
TABLE
WHERE
...
How can I select just one column value while I group the rows by other columns, please?
;WITH CTE AS
(
SELECT * , ROW_NUMBER() OVER (PARTITION BY COLOR,[TYPE] ORDER BY [DATE] DESC) rn
FROM TableName
)
SELECT ID
,COLOR
,[TYPE]
,[DATE]
FROM CTE
WHERE rn = 1
OR
SELECT ID
,COLOR
,[TYPE]
,[DATE]
FROM
(
SELECT * , ROW_NUMBER() OVER (PARTITION BY COLOR,[TYPE] ORDER BY [DATE] DESC) rn
FROM TableName
) A
WHERE rn = 1

Resources