SQL Query, SELECT Top 2 by Foreign Key Order By Date - sql-server-2005

I need a SQL query that returns the top 2 Plans by PlanDate per ClientID. This is all on one table where PlanID is the PrimaryID, ClientID is a foreignID.
This is what I have so far -->
SELECT *
FROM [dbo].[tblPlan]
WHERE [PlanID] IN (SELECT TOP (2) PlanID FROM [dbo].[tblPlan] ORDER BY [PlanDate] DESC)
This, obviously, only returns 2 records where I actually need up to 2 records per ClientID.

This can be done using ROW_NUMBER:
SELECT PlanId, ClientId, PlanDate FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ClientId ORDER BY PlanDate DESC) rn, *
FROM [dbo].[tblPlan]
) AS T1
WHERE rn <=2
Add any other columns you need to the select to get those too.

Edit, Dec 2011. Corrected CROSS APPLY solution
Try both to see what is best
SELECT *
FROM
( -- distinct ClientID values
SELECT DISTINCT ClientID
FROM [dbo].[tblPlan]
) P1
CROSS APPLY
( -- top 2 per ClientID
SELECT TOP (2) P2.PlanID
FROM [dbo].[tblPlan] P2
WHERE P1.ClientID = P2.ClientID
ORDER BY P2.[PlanDate] DESC
) foo
Or
;WITH cTE AS (
SELECT
*,
ROW_NUMBER () OVER (PARTITION BY clientid ORDER BY [PlanDate] DESC) AS Ranking
FROM
[dbo].[tblPlan]
)
SELECT * FROM cTE WHERE Ranking <= 2

Related

Select every second record then determine earliest date

I have table that looks like the following
I have to select every second record per PatientID that would give the following result (my last query returns this result)
I then have to select the record with the oldest date which would be the following (this is the end result I want)
What I have done so far: I have a CTE that gets all the data I need
WITH cte
AS
(
SELECT visit.PatientTreatmentVisitID, mat.PatientMatchID,pat.PatientID,visit.RegimenDate AS VisitDate,
ROW_NUMBER() OVER(PARTITION BY mat.PatientMatchID, pat.PatientID ORDER BY visit.VisitDate ASC) AS RowNumber
FROM tblPatient pat INNER JOIN tblPatientMatch mat ON mat.PatientID = pat.PatientID
LEFT JOIN tblPatientTreatmentVisit visit ON visit.PatientID = pat.PatientID
)
I then write a query against the CTE but so far I can only return the second row for each patientID
SELECT *
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate, RowNumber FROM cte
) as X
WHERE RowNumber = 2
How do I return the record with the oldest date only? Is there perhaps a MIN() function that I could be including somewhere?
If I follow you correctly, you can just order your existing resultset and retain the top row only.
In standard SQL, you would write this using a FETCH clause:
SELECT *
FROM (
SELECT
visit.PatientTreatmentVisitID,
mat.PatientMatchID,
pat.PatientID,
visit.RegimenDate AS VisitDate,
ROW_NUMBER() OVER(PARTITION BY mat.PatientMatchID, pat.PatientID ORDER BY visit.VisitDate ASC) AS rn
FROM tblPatient pat
INNER JOIN tblPatientMatch mat ON mat.PatientID = pat.PatientID
LEFT JOIN tblPatientTreatmentVisit visit ON visit.PatientID = pat.PatientID
) t
WHERE rn = 2
ORDER BY VisitDate
OFFSET 0 ROWS FETCH FIRST 1 ROW ONLY
This syntax is supported in Postgres, Oracle, SQL Server (and possibly other databases).
If you need to get oldest date from all selected dates (every second row for each patient ID) then you can try window function Min:
SELECT * FROM
(
SELECT *, MIN(VisitDate) OVER (Order By VisitDate) MinDate
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,
RowNumber FROM cte
) as X
WHERE RowNumber = 2
) Y
WHERE VisitDate=MinDate
Or you can use SELECT TOP statement. The SELECT TOP clause allows you to limit the number of rows returned in a query result set:
SELECT TOP 1 PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate FROM
(
SELECT *
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate,
RowNumber FROM cte
) as X
WHERE RowNumber = 2
) Y
ORDER BY VisitDate
For simplicity add order desc on date column and use TOP to get the first row only
SELECT TOP 1 *
FROM
(
SELECT PatientTreatmentVisitID,PatientMatchID,PatientID, VisitDate, RowNumber FROM cte
) as X
WHERE RowNumber = 2
order by VisitDate desc

Select Max revenue by date and client

The following is sample data from my table:
Rev Date Client
1890 2015-11-20 xyz
1536.28 2017-10-27 AAA
934.84 2017-10-27 AAA
919.592 2017-03-22 AAA
760.985 2014-11-25 xyz
I need to select the day with the highest revenue for each client.
I have the following query but it selects the max revenue for each day by client rather than just the day with the highest rev per client
SELECT TOP 1 max(rev)/1000 AS Rev, date, client FROM table1 GROUP BY date, client
Common question. I presume that Rev is already totaled per day and we're just picking out the highest row. If there are ties you could use dense_rank() instead.
select * from (
select *, row_number() over (partition by Client order by Rev desc) as rn
from table1
) t
where rn = 1;
One option is to use the the WITH TIES clause in concert with Row_Number()
Example
Select top 1 with ties *
From YourTable
Order By Row_Number() over (Partition By Client Order By Rev Desc)
Setup:
DECLARE #data TABLE
(
Rev money
, [Date] date
, Client nvarchar(10)
)
INSERT INTO #data
VALUES
(1890, '2015-11-20', 'xyz')
,(1536.28, '2017-10-27', 'AAA')
,(934.84, '2017-10-27', 'AAA')
,(919.592, '2017-03-22', 'AAA')
,(760.985, '2014-11-25', 'xyz')
Query:
SELECT Client
, t.[Date]
FROM
(
SELECT Client
, [Date]
, SUM(Rev) AS [TotalRev]
, ROW_NUMBER() OVER (PARTITION BY Client ORDER BY SUM(Rev) DESC) AS RN
FROM #data
GROUP BY Client, [Date]
) AS t
WHERE t.RN = 1
Results:
Client Date
====================
AAA 2017-10-27
xyz 2015-11-20
My understanding michaelg's question is per day the highest revenue transactions, so:
select * from (
select *, row_number() over (partition by [date] order by Rev desc) as rn
from ##table1
) t
where rn = 1;
If you want total amount of your client, you have group all rev into subquery.
Please try your script in my T-SQL Code playground https://sqleditor.net/q/B19r72H_f
I would use CROSS APPLY for this.
SELECT DISTINCT ca.*
FROM table1 t
CROSS APPLY (SELECT TOP 1 *
FROM table1
WHERE Client = t.Client
ORDER BY Rev DESC) ca
It's cleaner if you already have a Client table that has unique ids.
SELECT ca.Rev, ca.Date, c.Client
FROM Client c
CROSS APPLY (SELECT TOP 1 Rev, Date
FROM table1
WHERE Client = c.Client
ORDER BY Rev DESC) ca

Filter the table with latest date having duplicate OrderId

I have following table:
I need to filter out the rows for which start date is latest corresponding to its order id .With reference to given table row no 2 and 3 should be the output.
As row 1 and row 2 has same order id and order date but start date is later than first row. And same goes with row number 3 and 4 hence I need to take out row no 3 . I am trying to write the query in SQL server. Any help is appreciated.Please let me know if you need more details.Apologies for poor English
You can do this easily with a ROW_NUMBER() windowed function:
;With Cte As
(
Select *,
Row_Number() Over (Partition By OrderId Order By StartDate Desc) RN
From YourTable
)
Select *
From Cte
Where RN = 1
But I question the StartDate datatype. It looks like these are being stored as VARCHAR. If that is the case, you need to CONVERT the value to a DATETIME:
;With Cte As
(
Select *,
Row_Number() Over (Partition By OrderId
Order By Convert(DateTime, StartDate) Desc) RN
From YourTable
)
Select *
From Cte
Where RN = 1
Another way using a derived table.
select
t.*
from
YourTable t
inner join
(select OrderId, max(StartDate) dt
from YourTable
group by OrderId) t2 on t2.dt = t.StartDate and t2.OrderId = t.OrderId

Opposite of TOP in SQL Server

I need to retrieve the last few entries from a table. I can retrieve them using:
SELECT TOP n *
FROM table
ORDER BY id DESC
That I looked everywhere and that's the only answer I could find, But that way I get them in reverse order. I need them in the same order as they are in the table because it's for a messaging interface.
Use a derived table:
select id, ...
from
(
select top n id, ...
from t
order by id desc
) dt
order by id
I suggest you to use a ROW_NUMBER() like this:
SELECT *
FROM (
SELECT
*, ROW_NUMBER() OVER (ORDER BY id DESC) AS RowNo
FROM
yourTable
) AS t
WHERE
(RowNO < #n)
ORDER BY
id

Multiple grouping by

Lets say I have a table with business listings.
I'd like my result set to show in this order, but with one query:
First: Show the 5 most recently created listings in order of created_date.
Then: Show the rest of them but in random order.
My fields:
[BusinessName]
[Date_Created]
So if I had 100 businesses in the table, I want the list to show the 5 most recently created ones, and then show the rest but in random order.
Thank you in advance for your help!
Option A - Separating top 5 and the rest into two sub-queries and selecting them with UNION
WITH CTE_TOP5 AS
(
SELECT TOP 5 BusinessName, Date_Created, ROW_NUMBER() OVER (ORDER BY DATE_CREATED DESC) RN FROM dbo.YourTable
ORDER BY Date_Created DESC
)
, CTE_REST AS
(
SELECT BusinessName, Date_Created FROM dbo.YourTable
EXCEPT
SELECT BusinessName, Date_Created FROM CTE_TOP5
)
,CTE_RESTRANDOM AS
(
SELECT BusinessName, Date_Created, ROW_NUMBER() OVER (ORDER BY NEWID()) + 5 RN FROM CTE_REST
)
SELECT * FROM CTE_TOP5
UNION ALL
SELECT * FROM CTE_RESTRANDOM
ORDER BY RN
Option B - CASE in ORDER BY
;WITH CTE_TOP5 AS
(
SELECT TOP 5 *, ROW_NUMBER() OVER (ORDER BY DATE_CREATED DESC) RN FROM dbo.YourTable
ORDER BY Date_Created DESC
)
SELECT yt.*
FROM dbo.YourTable yt
LEFT JOIN CTE_TOP5 t5 ON yt.BusinessName = t5.BusinessName
AND yt.Date_Created = t5.Date_Created
ORDER BY CASE WHEN t5.RN IS NOT NULL THEN t5.RN ELSE 6 END, NEWID()
Option C - Similar like B, but no CTE, ROW_NUMBERS and JOINS - whole logic goes in ORDER BY
SELECT *
FROM dbo.YourTable yt
ORDER BY CASE WHEN yt.Date_Created IN (SELECT TOP 5 yt2.Date_Created
FROM dbo.YourTable yt2
ORDER BY yt2.Date_Created DESC)
THEN yt.Date_Created
ELSE '1900-01-01'
END DESC, NEWID()