Reputation: 4190
I have a table that contains a record of prioritys that links to another table.
FK - Priority - PriorityUpdateDateTime
What I'm trying to do is add an end date to this table, but only as a temp table.
So what I need is FK - Priority - StartDate - EndDate
Where EndDate is the next PriorityUpdateDateTime
i.e.
FK - Priority - PriorityUpdateDateTime
123 - 4 - 2011-02-25
123 - 2 - 2011-02-30
123 - 3 - 2011-03-10
Becomes
FK - Priority - StartDate - EndDate
123 - 4 - 2011-02-25 - 2011-02-30
123 - 2 - 2011-02-30 - 2011-03-10
123 - 3 - 2011-03-10 - NULL
Thanks for your help
Upvotes: 1
Views: 1310
Reputation: 3494
@Matt @Kane
Kane did fine except he picked the wrong column for the inequality. I just made a couple of corrections. I'd be surprised if someone can come up with a faster method other than maybe a "Quirky Update" which would blow the doors off the correlated sub-query and CROSS APPLY methods.
Here's the code to build more test data than you can shake a stick at for performance testing purposes.
--===== Conditionallly drop the test table to make reruns in SSMS easier
IF OBJECT_ID('tempdb..#Test','U') IS NOT NULL
DROP TABLE #Test
;
--===== Create and populate the test table on-the-fly
-- using a "Pseudo Cursor" which is many times
-- faster than a WHILE loop.
SELECT TOP 100000
FK = ABS(CHECKSUM(NEWID()))%100+100, -- 100 thru 199
Priority = ABS(CHECKSUM(NEWID()))%100+1, -- 1 thru 100
PriorityUpdateDateTime = DATEADD(dd,
ABS(CHECKSUM(NEWID()))%DATEDIFF(dd,'2000','2010')
,'2000') --20000101 thru 20091231
INTO #Test
FROM sys.all_columns ac1 --has more than 4000 rows even on a new system
CROSS JOIN sys.all_columns ac2
;
--===== Create a clustered index to improve performance by about 10 times in this case
CREATE INDEX IX_#Test ON #Test (FK,PriorityUpdateDateTime)
;
Here are two different renditions of Kane's code. The details are in the code. Both return the same result in about the same amount of time.
--===== Kane's correlated subquery works just fine here once we
-- flip it around and use a different column name in the
-- inequality part.
SELECT t1.FK,
t1.Priority,
StartDate = t1.PriorityUpdateDateTime,
EndDate =
(
SELECT MIN(t2.PriorityUpdateDateTime)
FROM #Test t2
WHERE t2.FK = t1.FK
AND t2.PriorityUpdateDateTime > t1.PriorityUpdateDateTime
)
FROM #Test t1
ORDER BY t1.FK, t1.PriorityUpdateDateTime, t1.Priority
;
--===== Or, you could use a CROSS APPLY and get the same thing because
-- a CROSS APPLY isn't much more than a correlated sub-query.
SELECT t1.FK,
t1.Priority,
StartDate = t1.PriorityUpdateDateTime,
d.EndDate
FROM #Test t1
CROSS APPLY
(
SELECT MIN(t2.PriorityUpdateDateTime)
FROM #Test t2
WHERE t2.FK = t1.FK
AND t2.PriorityUpdateDateTime > t1.PriorityUpdateDateTime
) d (EndDate)
ORDER BY t1.FK, t1.PriorityUpdateDateTime, t1.Priority
;
Upvotes: 1
Reputation: 16802
Ok you can use a sub select like this:
-- nasty sub select, lets see if we can do better
SELECT T1.[FK],
T1.[Priority],
T1.[PriorityUpdateDateTime] AS 'StartDate',
(
SELECT MAX(T2.[PriorityUpdateDateTime]) AS MP
FROM @test AS T2
WHERE T2.[FK] = T1.[FK]
AND T2.[Priority] < T1.[Priority]
) AS 'EndDate'
FROM @test AS T1
Here is my temporary script to create the testing table and data
DECLARE @test AS TABLE
(
[FK] INT NOT NULL,
[Priority] INT NOT NULL,
[PriorityUpdateDateTime] DATETIME NOT NULL
)
INSERT INTO @test VALUES(123, 4, '20110225')
INSERT INTO @test VALUES(123, 3, '20110228') -- there are only 28 days in Feb in 2011 (not 30)
INSERT INTO @test VALUES(123, 2, '20110310')
-- nasty sub select, lets see if we can do better
SELECT T1.[FK],
T1.[Priority],
T1.[PriorityUpdateDateTime] AS 'StartDate',
(
SELECT MAX(T2.[PriorityUpdateDateTime]) AS MP
FROM @test AS T2
WHERE T2.[FK] = T1.[FK]
AND T2.[Priority] < T1.[Priority]
)
FROM @test AS T1
Upvotes: 1