Query last event each person attended - sql

I have a table called CampRegistration with the following structure:
[EID] INT, [CampName] VARCHAR(100), [StartDt] DATETIME
EID is the identifier of the person who attended the camp. I wish to select the last camp each person in the CampRegistration table has attended prior to a given date.
Here was my first attempt:
#DECLARE #currentCampDt DATETIME = '2012-8-4';
SELECT [EID], [CampName], MAX([StartDt]) [StartDt]
FROM [CampRegistration]
WHERE [StartDt] < #currentCampDt
GROUP BY [EID],[CampName]
order by [EID]
The problem here is that if someone has attended multiple camps w/ different names I'll get multiple results for that person (the last attended of each camp name). I only wish to get a single record back for each person, the last camp of any name they attended. I do ultimately need to get those three pieces of info for each record (EID, CampName, and the camp's StartDt) so I'm not sure if I really can remove CampName from the Group By.
I'm using SQL Server 2012 and would greatly appreciate any suggestions on how to implement this type of query result.
Thanks.

One approach would be to use a CTE (Common Table Expression).
With this CTE, you can partition your data by some criteria - i.e. your EID - and have SQL Server number all your rows starting at 1 for each of those "partitions", ordered by some criteria.
So try something like this:
;WITH CampEvents AS
(
SELECT
EID, CampName, StartDt,
RowNum = ROW_NUMBER() OVER(PARTITION BY EID ORDER BY StartDt DESC)
FROM
dbo.CampRegistration
)
SELECT
EID, CampName, StartDt
FROM
CampEvents
WHERE
RowNum = 1
Here, I am selecting only the "first" entry for each "partition" (i.e. for each EID) - ordered by the descending StartDt - so the newest, most recent event has RowNum = 1.
Does that approach what you're looking for??

This won't be fast, but will do it:
#DECLARE #currentCampDt DATETIME = '2012-8-4';
SELECT o.[EID], o.[CampName], o.[StartDt]
FROM [CampRegistration] o
WHERE o.[StartDt] = (
SELECT MAX(i.[StartDt])
FROM [CampRegistration] i WHERE i.[StartDt] < #currentCampDt
AND
i.[EID] = o.[EID]
)
order by o.[EID]

You can do this:
WITH LastCampsAttended
As
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY EID ORDER BY StartDt DESC) AS rownum
FROM CampRegistration
WHERE [StartDt] < #currentCampDt
)
SELECT
EID,
CampName,
StartDt
FROM LastCampsAttended
WHERE rownum = 1;
SQL Fiddle Demo
Or:
SELECT
camps.*
FROM CampRegistration camps
INNER JOIN
(
SELECT EID, MAX(StartDt) LatestDate
FROM CampRegistration
GROUP BY EID
) LatestCamps ON camps.EID = LatestCamps.EID
AND camps.StartDt = LatestCamps.LatestDate
WHERE camps.StartDt < #currentCampDt ;
Updated SQL fiddle Demo

Related

Turn these temp tables into one longer subquery (can't use Temp tables in Power BI)

Currently I have created these temp tables to get the desired output I need. However, Power BI doesn't allow the use of temp tables so I need to get this all into 1 query using inner selects.
drop table if exists #RowNumber
Select Date, ID, ListID
, row_number() over (partition by ID order by ID) as rownum
into #RowNumber
from Table
where Date= cast(getdate()-1 as date)
group by Date, ID, ListID
order by ID
drop table if exists #1stListIDs
select ListID as FirstID, ID, Date
into #1stListIDs
from #RowNumber
where rownum = 1
drop table if exists #2ndlistids
Select ListID as SecondListID, ID, Date
into #2ndlistids
from #RowNumber
where rownum = 2
--Joins the Two Tables back together to allow the listids to be in the same row
drop table if exists #FinalTableWithTwoListIDs
select b.FirstListID, a.SecondListID, a.ID, a.Date
into #FinalTableWithTwoListIDs
from #2ndlistids a
join #1stListIDs b on a.ID= b.ID
order by ID
This code is simple and straight forward. However I can't seem to figure out using a subquery. Here is what I have. It works for the FirstListID select statement, but not the SecondListID portion. I believe this is because you can't reference the inner most select statement with multiple different outer select statements, but I could be wrong.
Select a.ListId as SecondListID, a.ID, a.Date
from (
select a.ListId as FirstListID, a.ID, a.Date
from (
Select Date, ID, ListId
, row_number() over (partition by ID order by ID) as rownum
from Table
where Date = cast(getdate()-1 as date)
group by Date, ID, ListId
order by ID) a
where a.rownum = 1) b
where a.rownum = 2) c
Just to show, for completeness, how you could use CTEs to replace the #temp tables, it would be something along the lines of
with RowNumber as (
select Date, ID, ListID
, row_number() over (partition by ID order by ID) as rownum
from Table
where Date= cast(dateadd(day,-1,getdate()) as date)
group by Date, ID, ListID
),
FirstListIDs as (
select ListID as FirstID, ID, Date
from RowNumber
where rownum = 1
),
SecondListIDs as (
select ListID as SecondID, ID, Date
from RowNumber
where rownum = 2
)
select f.FirstID, s.SecondID, s.ID, s.Date
from Secondlistids s
join FirstListIDs f on s.ID=f.ID
order by s.ID
Note the use of dateadd which is recommended over the ambiguousdate +/- value assumed to be days, and where relevant meaningful table aliases.
You could do it with a CTE and joining the two together, but that is inefficient and unnecessary.
It looks like you just need LAG to get the previous ListID
I note that PARTITION BY ID ORDER BY ID is non-deterministic and the ordering will be random. I strongly suggest you find a deterministic ordering.
SELECT
PrevID AS b.FirstListID,
ListID AS a.SecondListID,
ID,
Date
FROM (
SELECT
Date,
ID,
ListID,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY ID) AS rownum,
LAG(ListID) OVER (PARTITION BY ID ORDER BY ID) AS PrevID
from [Table]
where Date = cast(getdate() - 1 as date)
group by Date, ID, ListID
) AS WithRowAndLag
WHERE rownum = 2;
ORDER BY ID;

Select MAX Value for Each ROW - Oracle Sql

I have one doubt.
I need to find what is the latest occurrence for a specific list of Customers, let's say to simplify, I need it for 3 Customers out of 100.
I need to check when it was the last time each of them got a bonus.
The table would be:
EVENT_TBL
Fields: Account ID, EVENT_DATE, BONUS ID, ....
Can you suggest a way to grab the latest (MAX) EVENT DATE (that means one row each)
I'm using SELECT...IN to specify the Account ID but not sure how to use MAX, Group BY etc etc (if ever needed).
Use the ROW_NUMBER() analytic function:
SELECT *
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY Account_id ORDER BY event_date DESC ) AS rn
FROM EVENT_TBL t
WHERE Account_ID IN ( 123, 456, 789 )
)
WHERE rn = 1
You can try
with AccountID_Max_EVENT_DATE as (
select AccountID, max(EVENT_DATE) MAX_D
from EVENT_TBL
group by AccountID
)
SELECT E.*
FROM EVENT_TBL E
INNER JOIN AccountID_Max_EVENT_DATE M
ON (E.AccountID = M.AccountID AND M.MAX_D = E.EVENT_DATE)

How to select the latter row in SQL

I have a result set that looks like this:
As you can see some of the contactID are repeated with same QuestionResponse. And there is one with a different QuestionResponse (the one with red lines).
I want to group this by ContactID, but select the latter row. Eg: In case of ContactID = 78100299, I want to select the row with CreateDate = 17:00:44.907 (or rowNum = 2).
I have tried this:
select
ContactID,
max(QuestionResponse) as QuestionResponse,
max(CreateDate) as CreateDate
from
theResultSet
group by
ContactID
This will NOT work because there could be QuestionResponse 2 and then 1 for the same contactID. In that case the latter one will be the one with response 1 not 2.
Thank you for you help.
I would use ROW_NUMBER() that way:
WITH Query AS
(
SELECT rowNum, ContactID, QuestionResponse, CreateDate,
ROW_NUMBER() OVER (PARTITION BY ContactID ORDER BY CreateDate DESC) Ordered
FROM theResultSet
)
SELECT * FROM Query WHERE Ordered=1
Assign numbers in ContactID group by date, descending
Filter results having number <> 1
This might work if your SQL Engine can handle it...
SELECT trs1.*
FROM theResultSet trs1
INNER JOIN
(SELECT ContactID, max(CreateDate) as CreateDate
FROM theResultSet
GROUP BY ContactID) trs2
ON trs1.ContactID = trs2.ContactID
AND trs1.CreateDate = trs2.CreateDate
The end result will be all rows from theResultSet where the creation date is the max creation date.
This should work too:
SELECT
ContactID, QuestionResponse,CreateDate
FROM (
select rowNum, ContactID, QuestionResponse,CreateDate,
max(rowNum) over(partition by ContactID) as maxrow
from theResultSet
) x
WHERE rowNum=maxrow

How to grab data from the 2nd most recent record for each student?

Working in a school district I have a database that contains information about students' Education Plans. I'm using Management Studio to access a SQL Server 2012 database. I need to grab the information from their 2nd most recent plan. Here are the columns.
Database Name is PlansAnoka
Table name is dbo.plans
Columns:
PlanID (primary key)
StudentID (these would all be unique for each student)
PlanDate (this is the column that I want to use as the date to pull the 2nd most recent record.
Meeting Date (this is just another data point that I need)
I know how to create a query to grab the most recent one, but not the 2nd most recent one. AND I need 2nd most recent record for EACH student. Any help would be appreciated!
Use the ROW_NUMBER function and partition by StudentID:
WITH A AS
(
SELECT
StudentID ,
PlanDate ,
MeetingDate,
ROW_NUMBER() OVER(PARTITION BY StudentID ORDER BY PlanDate DESC) rownum
FROM dbo.plans
)
SELECT * FROM a
WHERE rownum=2
use row_number if you want get n'th record:
select * from
(select studentid ,
plandata ,
row_number() over(partition by plandata order by plandata desc) rn
from dbo.plans) t
where t.rn=2 -- or n
Please try this:
select * from
(select *, row_number() over (partition by StudentID order by PlanDate desc) Rank
from dbo.plans) p
where p.Rank = 2
Reference link: https://msdn.microsoft.com/en-us/library/ms186734.aspx
Try this one;
select * from
(
select *, ROW_NUMBER()
over(partition by StudentID order by plandate desc)
as Row from dbo.plans
) temp
WHERE temp.Row = 2

Optimizing query with two MAX columns in the same table

I need to optimize below query
SELECT
Id, -- identity
CargoID,
[Status] AS CurrentStatus
FROM
dbo.CargoStatus
WHERE
id IN (SELECT TOP 1 ID
FROM dbo.CargoStatus CS
INNER JOIN STD.StatusMaster S ON CS.ShipStatusID = S.SatusID
WHERE CS.CargoID=CargoStatus.CargoID
ORDER BY YEAR([CS.DATE]) DESC, MONTH([CS.DATE]) DESC,
DAY([CS.DATE]) DESC, S.StatusStageNumber DESC)
There are two tables
CargoStatus, and
StatusMaster
Statusmaster has columns StatusID, StatusName, StatusStageNumber(int)
CargoStatus has columns ID, StatusID (FK StatusMaster StatusID column), Date
Is there any other better way of writing this query.
I want latest status for each cargo (only one entry per cargoID).
Since you seem to be using SQL Server 2005 or newer, you can use a CTE with the ROW_NUMBER() windowing function:
;WITH LatestCargo AS
(
SELECT
cs.Id, -- identity
cs.CargoID,
cs.[Status] AS CurrentStatus
ROW_NUMBER() OVER(PARTITION BY cs.CargoID
ORDER BY cs.[Date], s.StatusStageNumber DESC) AS 'RowNum'
FROM
dbo.CargoStatus cs
INNER JOIN
STD.StatusMaster s ON cs.ShipStatusID = s.[StatusID]
)
SELECT
Id, CargoID, [Status]
FROM
LatestCargo
WHERE
RowNum = 1
This CTE "partitions" your data by CargoID, and for each partition, the ROW_NUMBER function hands out sequential numbers, starting at 1 and ordered by Date DESC - so the latest row gets RowNum = 1 (for each CargoID) which is what I select from the CTE in the SELECT statement after it.