calculate difference between two times in two rows in sql - sql-server

I am using MSSQL 2008 Standard
I have multiple rows in a select command which are filled with events. For every event I have got a timestamp, now I want to calculate the time between the events:
(number) | event | timestamp | duration
---------+----------------+---------------------+----------
1 | logon | 2012-05-23 10:00:00 |
2 | incomming call | 2012-05-23 10:01:00 |
3 | call ended | 2012-05-23 10:02:00 |
4 | logoff | 2012-05-23 10:04:00 |
(the number column does not exist but it's easier for explanation)
Now the duration cell for the first row should be 1, for the second one also 1 and for the third one 2.
Does anybody know how to achieve this without loops and so on.
Thank you

You need a self join. Since you need to generate an id then something like:
select t1.*, datediff(minute, t2.timestamp, t1.timestamp) from
(select *, row_number() over (order by ...) as rowid from MyTable) t1
inner join
(select *, row_number() over (order by ...) as rowid from MyTable) t2
on t1.rowid = t2.rowid - 1

I found the CTE answer provided less than desirable due to its not reporting the first line. I found the other answers with join's too complex. I distilled the problem into this snippet
Here is the code which uses a CTE, creates a sequence within the CTE's select which identifies a row number by an ordered timestamp. The resulting selection picks on the resulting ordered rows and determines minutes.
WITH AgentActions AS
(
select ROW_NUMBER() OVER (ORDER BY [TimeStamp]) -- Create an index number ordered by time.
AS [Sequence],
* from AgentInteractions
)
SELECT *,
ISNULL(DATEDIFF(Minute,
(SELECT other.TimeStamp
FROM AgentActions Other
WHERE other.Sequence = AgentActions.Sequence - 1 ),
AgentActions.TimeStamp),
0)
AS MinutesFromLastPoint
FROM AgentActions;
Here is the setup table
CREATE TABLE AgentInteractions
(
[Event] VARCHAR(12) NOT NULL,
[Timestamp] [DateTime] NOT NULL
);
INSERT INTO dbo.AgentInteractions( Event, TimeStamp )
VALUES ( 'Alpha', '1-Jan-2018 3:04:22 PM' ),
( 'Omega', '3-Jan-2018 10:04:22 PM' ),
( 'Beta', '2-Jan-2018 2:04:22 AM' );
Results
SQL Fiddle Example

This is my current version/solution:
declare #temp table
(
id int,
timestamp datetime,
type nvarchar(255),
skillname nvarchar(255),
event nvarchar(255),
userstatus nvarchar(255)
)
insert into #temp (id, timestamp, type, skillname, event, userstatus)
(
select ROW_NUMBER() over (order by timestamp) as id, * from
(
select TimeStamp, 'Event' as type, SkillName, Event, UserStatus from AgentEvents
where TimeStamp >= '2012-05-22T00:00:00'
and UserName like '%engel%'
union
select TimeStamp, 'Anruf' as type, SkillName, '' as event, '' as status from calls
where TimeStamp >= '2012-05-22T00:00:00'
and UserName like '%engel%'
) as a
)
select t1.*, DATEDIFF(second, t1.timestamp, t2.timestamp) as duration
from #temp t1
left outer join #temp t2 on t1.id = t2.id - 1
Edit: changed inner join to left outer join, otherwise the last row would be lost.

As I understand it, you need to update the duration column.
You can use something like this :
update mytable a set duration = DateDiff( a.timestamp, select top b.timestamp from mytable b order by b.timestamp asc)
I cannot test it, but just to give you an idea (it may have some syntax errors).
Using the 'top' with the 'order by' clause should do the trick.
(Edited)

I think you better create a trigger
CREATE TRIGGER update_duration ON sometable
INSTEAD OF INSERT
AS
DECLARE #lastDT datetime
BEGIN
SET #lastDT =
(SELECT TOP 1 _timestamp
FROM sometable
ORDER BY _timestamp DESC)
UPDATE sometable
SET duration = DATEDIFF(MINUTE, #lastDT, GETDATE())
END

WITH rows AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY Col1) AS rn
FROM dbo.Table_2
)
SELECT mc.col1, DATEDIFF(HOUR, mc.Col1, mp.Col1) as TimeDiffInHours
FROM rows mc
JOIN rows mp
ON mc.rn = mp.rn-1

Related

Loops on SQL Server

I have the following query where I input a date and it give me the result. However, I need to run this for 60 different dates. Instead of running this 1 by 1, is there anyway to automate this so it runs each time on a different date?
IF OBJECT_ID('tempdb..#1') IS NOT NULL DROP TABLE #1
declare #d1 datetime = '2020-02-06'
select distinct [User] into #1
from [X].[dbo].[Table1]
where [status] = 'Success'
and [Date] = #d1;
select count(distinct [User])
from #1
inner join [Y].[dbo].[Table2]
on #1.[User] = [Y].[dbo].[Table2].User
where [Date2] between #d1 and #d1+1
and [Checkname] in ('Check1','Check2')
Loops are slow and generally a bad practice in the context of T-SQL. You can use something like this to get the count of users for a batch of dates:
DROP TABLE IF EXISTS #DataSource;
CREATE TABLE #DataSource
(
[Date] DATETIME
,[UsersCount] INT
);
INSERT INTO #DataSource ([Date])
VALUES ('2020-02-06')
,('2020-02-07')
,('2020-02-08');
IF OBJECT_ID('tempdb..#1') IS NOT NULL DROP TABLE #1
select distinct DS1.[Date]
,DS1.[User]
into #1
from [X].[dbo].[Table1] DS1
INNER JOIN #DataSource DS2
ON DS1.[Date] = DS2.[Date]
where DS1.[status] = 'Success';
select #1.[date]
,count(distinct [User])
from #1
inner join [Y].[dbo].[Table2]
on #1.[User] = [Y].[dbo].[Table2].User
where [Date2] between #1.[date] and #1.[date] + 1
and [Checkname] in ('Check1','Check2')
GROUP BY #1.[date]
First, I want to say that gotqn's answer is a good answer - however, I think there are a few more things in the original code that can be improved - so here is how I would probably do it:
Assuming the dates are consecutive, use a common table expression to calculate the dates using dateadd and row_number.
Then, use another common table expression to get the list of dates and users from table1,
and then select the date and count of distinct users for each date from that common table expression joined to table2:
DECLARE #StartDate Date = '2020-02-06';
WITH Dates AS
(
SELECT TOP (60) DATEADD(DAY, ROW_NUMBER() OVER(ORDER BY ##SPID) -1, #StartDate) As Date
FROM sys.objects
), CTE AS
(
SELECT t1.[User], t1.[Date]
FROM [X].[dbo].[Table1] AS t1
JOIN Dates
ON t1.[Date] = Dates.[Date]
WHERE [status] = 'Success'
)
SELECT cte.[Date], COUNT(DISTINCT [User])
FROM CTE
JOIN [Y].[dbo].[Table2] As t1
ON CTE.[User] = t1.[User]
AND t1.[Date2] >= CTE.[Date]
AND t1.[Date2] < DATEADD(Day, 1, CTE.[Date])
AND [Checkname] IN ('Check1','Check2')
GROUP BY cte.[Date]
If the dates are not consecutive, you can use a table variable to hold the dates instead of calculating them using a common table expression.

How to select highest common value across groups

`Suppose I have a set of data with 2 fields - Type and Date. I am interested in finding (if exists) the the max common date across the various types. Is this easier to do in SQL or LINQ?
Given the data below the result should be 2018-02-01 as this is the max common date for all types. It there is no such date then no data is returned.
Type, Date
---------
1,2018-03-01
1,2018-02-01
1,2018-01-01
2,2018-02-01
2,2018-05-01
2,2018-01-01
3,2018-01-01
3,2018-03-01
3,2018-02-01
You could use:
SELECT TOP 1 [Date], COUNT(*) OVER(PARTITION BY Date) AS cnt
FROM tab
ORDER BY cnt DESC, [Date] DESC
DBFiddle Demo
This'll work if you have an unlimited or indeterminable number of Types:
CREATE TABLE #Sample ([Type] int, [DAte] date);
INSERT INTO #Sample
VALUES
(1,'20180301'),
(1,'20180201'),
(1,'20180101'),
(2,'20180201'),
(2,'20180501'),
(2,'20180101'),
(3,'20180101'),
(3,'20180301'),
(3,'20180201');
GO
WITH EntryCount AS(
SELECT [Type], [Date],
COUNT(*) OVER (PARTITION By [Date]) AS Entries
FROM #Sample)
SELECT MAX(Date)
FROM EntryCount EC
WHERE Ec.Entries = (SELECT COUNT(DISTINCT sq.[Type]) FROM #Sample sq);
GO
DROP TABLE #Sample;
Not sure how quick it'll be either though.
Example
Select Top 1 [Date]
from YourTable
Group By [Date]
Order By count([Type]) desc,[Date] desc
Returns
2018-02-01
This is not going to be very efficient not matter how you slice it because you have to compare across three groups. Assuming you have 3 types you could use a self join. Something like this.
select MAX(YourDate)
from YourTable yt
join YourTable yt2 on yt2.YourType = 2 and yt.YourDate = yt2.YourDate
join YourTable yt3 on yt3.YourType = 3 and yt.YourDate = yt3.YourDate
where yt.YourType = 1

SQL Update with row_number()

I want to update my column CODE_DEST with an incremental number. I have:
CODE_DEST RS_NOM
null qsdf
null sdfqsdfqsdf
null qsdfqsdf
I would like to update it to be:
CODE_DEST RS_NOM
1 qsdf
2 sdfqsdfqsdf
3 qsdfqsdf
I have tried this code:
UPDATE DESTINATAIRE_TEMP
SET CODE_DEST = TheId
FROM (SELECT Row_Number() OVER (ORDER BY [RS_NOM]) AS TheId FROM DESTINATAIRE_TEMP)
This does not work because of the )
I have also tried:
WITH DESTINATAIRE_TEMP AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE DESTINATAIRE_TEMP SET CODE_DEST=RN
But this also does not work because of union.
How can I update a column using the ROW_NUMBER() function in SQL Server 2008 R2?
One more option
UPDATE x
SET x.CODE_DEST = x.New_CODE_DEST
FROM (
SELECT CODE_DEST, ROW_NUMBER() OVER (ORDER BY [RS_NOM]) AS New_CODE_DEST
FROM DESTINATAIRE_TEMP
) x
DECLARE #id INT
SET #id = 0
UPDATE DESTINATAIRE_TEMP
SET #id = CODE_DEST = #id + 1
GO
try this
http://www.mssqltips.com/sqlservertip/1467/populate-a-sql-server-column-with-a-sequential-number-not-using-an-identity/
With UpdateData As
(
SELECT RS_NOM,
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE DESTINATAIRE_TEMP SET CODE_DEST = RN
FROM DESTINATAIRE_TEMP
INNER JOIN UpdateData ON DESTINATAIRE_TEMP.RS_NOM = UpdateData.RS_NOM
Your second attempt failed primarily because you named the CTE same as the underlying table and made the CTE look as if it was a recursive CTE, because it essentially referenced itself. A recursive CTE must have a specific structure which requires the use of the UNION ALL set operator.
Instead, you could just have given the CTE a different name as well as added the target column to it:
With SomeName As
(
SELECT
CODE_DEST,
ROW_NUMBER() OVER (ORDER BY [RS_NOM] DESC) AS RN
FROM DESTINATAIRE_TEMP
)
UPDATE SomeName SET CODE_DEST=RN
This is a modified version of #Aleksandr Fedorenko's answer adding a WHERE clause:
UPDATE x
SET x.CODE_DEST = x.New_CODE_DEST
FROM (
SELECT CODE_DEST, ROW_NUMBER() OVER (ORDER BY [RS_NOM]) AS New_CODE_DEST
FROM DESTINATAIRE_TEMP
) x
WHERE x.CODE_DEST <> x.New_CODE_DEST AND x.CODE_DEST IS NOT NULL
By adding a WHERE clause I found the performance improved massively for subsequent updates. Sql Server seems to update the row even if the value already exists and it takes time to do so, so adding the where clause makes it just skip over rows where the value hasn't changed. I have to say I was astonished as to how fast it could run my query.
Disclaimer: I'm no DB expert, and I'm using PARTITION BY for my clause so it may not be exactly the same results for this query. For me the column in question is a customer's paid order, so the value generally doesn't change once it is set.
Also make sure you have indexes, especially if you have a WHERE clause on the SELECT statement. A filtered index worked great for me as I was filtering based on payment statuses.
My query using PARTITION by
UPDATE UpdateTarget
SET PaidOrderIndex = New_PaidOrderIndex
FROM
(
SELECT PaidOrderIndex, SimpleMembershipUserName, ROW_NUMBER() OVER(PARTITION BY SimpleMembershipUserName ORDER BY OrderId) AS New_PaidOrderIndex
FROM [Order]
WHERE PaymentStatusTypeId in (2,3,6) and SimpleMembershipUserName is not null
) AS UpdateTarget
WHERE UpdateTarget.PaidOrderIndex <> UpdateTarget.New_PaidOrderIndex AND UpdateTarget.PaidOrderIndex IS NOT NULL
-- test to 'break' some of the rows, and then run the UPDATE again
update [order] set PaidOrderIndex = 2 where PaidOrderIndex=3
The 'IS NOT NULL' part isn't required if the column isn't nullable.
When I say the performance increase was massive I mean it was essentially instantaneous when updating a small number of rows. With the right indexes I was able to achieve an update that took the same amount of time as the 'inner' query does by itself:
SELECT PaidOrderIndex, SimpleMembershipUserName, ROW_NUMBER() OVER(PARTITION BY SimpleMembershipUserName ORDER BY OrderId) AS New_PaidOrderIndex
FROM [Order]
WHERE PaymentStatusTypeId in (2,3,6) and SimpleMembershipUserName is not null
I did this for my situation and worked
WITH myUpdate (id, myRowNumber )
AS
(
SELECT id, ROW_NUMBER() over (order by ID) As myRowNumber
FROM AspNetUsers
WHERE UserType='Customer'
)
update AspNetUsers set EmployeeCode = FORMAT(myRowNumber,'00000#')
FROM myUpdate
left join AspNetUsers u on u.Id=myUpdate.id
Simple and easy way to update the cursor
UPDATE Cursor
SET Cursor.CODE = Cursor.New_CODE
FROM (
SELECT CODE, ROW_NUMBER() OVER (ORDER BY [CODE]) AS New_CODE
FROM Table Where CODE BETWEEN 1000 AND 1999
) Cursor
If table does not have relation, just copy all in new table with row number and remove old and rename new one with old one.
Select RowNum = ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) , * INTO cdm.dbo.SALES2018 from
(
select * from SALE2018) as SalesSource
In my case I added a new column and wanted to update it with the equevilat record number for the whole table
id name new_column (ORDER_NUM)
1 Ali null
2 Ahmad null
3 Mohammad null
4 Nour null
5 Hasan null
6 Omar null
I wrote this query to have the new column populated with the row number
UPDATE My_Table
SET My_Table.ORDER_NUM = SubQuery.rowNumber
FROM (
SELECT id ,ROW_NUMBER() OVER (ORDER BY [id]) AS rowNumber
FROM My_Table
) SubQuery
INNER JOIN My_Table ON
SubQuery.id = My_Table.id
after executing this query I had 1,2,3,... numbers in my new column
I update a temp table with the first occurrence of part where multiple parts can be associated with a sequence number. RowId=1 returns the first occurence which I join the tmp table and data using part and sequence number.
update #Tmp
set
#Tmp.Amount=#Amount
from
(SELECT Part, Row_Number() OVER (ORDER BY [Part]) AS RowId FROM #Tmp
where Sequence_Num=#Sequence_Num
)data
where data.Part=#Tmp.Part
and data.RowId=1
and #Tmp.Sequence_Num=#Sequence_Num
I don't have a running ID in order to do what "Basheer AL-MOMANI" suggested.
I did something like this: (joined my table on myself, just to get the Row Number)
update T1 set inID = T2.RN
from (select *, ROW_NUMBER() over (order by ID) RN from MyTable) T1
inner join (select *, ROW_NUMBER() over (order by ID) RN from MyTable) T2 on T2.RN = T1.RN

How to join sequential numbers to unrelated data (SQL Server)

This question is a followup to a previous question I had about discovering unused sequential number ranges without having to resort to cursors (Working with sequential numbers in SQL Server 2005 without cursors). I'm using SQL Server 2005.
What I need to do with those numbers is to assign those numbers to records in a table. I just can't seem to come up with a way to actually relate the numbers table with the records that need those numbers.
One possible solution that came to mind was insert the records in a temp table using an identity and using the beginning of the number range as an identity seed. The only problem with this approach is that if there are gaps in the number sequence then I'll end up with duplicate control numbers.
This is how my tables look like (overly simplified):
Numbers table:
Number
-------
102314
102315
102319
102320
102324
102329
Data table:
CustomerId PaymentAmt ControlNumber
---------- ---------- -------------
1001 4502.01 NULL
1002 890.00 NULL
9830 902923.34 NULL
I need a way to make it so i end up with:
CustomerId PaymentAmt ControlNumber
---------- ---------- -------------
1001 4502.01 102314
1002 890.00 102315
9830 902923.34 102319
Is this possible without having to use cursors? The reason I'm avoiding cursors is because our current implementation uses cursors and since its so slow (8 minutes over 12,000 records) I was looking for alternatives.
Note: Thanks to all who posted answers. All of them were great, I had to pick the one that seemed easier to implement and easiest to maintain for whomever comes after me. Much appreciated.
Try this:
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER(ORDER BY CustomerId) Corr
FROM DataTable
)
UPDATE CTE
SET CTE.ControlNumber = B.Number
FROM CTE
JOIN ( SELECT Number, ROW_NUMBER() OVER(ORDER BY Number) Corr
FROM NumberTable) B
ON CTE.Corr = B.Corr
Buidling on Martin's code from the linked question, you could give all rows without control number a row number. Then give all unused numbers a row number. Join the two sets together, and you get a unique number per row:
DECLARE #StartRange int, #EndRange int
SET #StartRange = 790123401
SET #EndRange = 790123450;
; WITH YourTable(ControlNumber, CustomerId) AS
(
SELECT 790123401, 1000
UNION ALL SELECT 790123402, 1001
UNION ALL SELECT 790123403, 1002
UNION ALL SELECT 790123406, 1003
UNION ALL SELECT NULL, 1004
UNION ALL SELECT NULL, 1005
UNION ALL SELECT NULL, 1006
)
, YourTableNumbered(rn, ControlNumber, CustomerId) AS
(
select row_number() over (
partition by IsNull(ControlNumber, -1)
order by ControlNumber)
, *
from YourTable
)
, Nums(N) AS
(
SELECT #StartRange
UNION ALL
SELECT N+1
FROM Nums
WHERE N < #EndRange
)
, UnusedNums(rn, N) as
(
select row_number() over (order by Nums.N)
, Nums.N
from Nums
where not exists
(
select *
from YourTable yt
where yt.ControlNumber = Nums.N
)
)
select ytn.CustomerId
, IsNull(ytn.ControlNumber, un.N)
from YourTableNumbered ytn
left join
UnusedNums un
on un.rn = ytn.rn
OPTION (MAXRECURSION 0)
All you need is a deterministic order in data table. If you have that, you can use ROW_NUMBER() as a join condition:
with cte as (
select row_number() over (order by CustomerId) as [row_number],
ControlNumber
from [Data Table]
where ControlNumber is null),
nte as (
select row_number() over (order by Number) as [row_number],
Number
from [Numbers])
update cte
set ControlNumber = Number
from cte
join nte on nte.[row_number] = cte.[row_number];
If you need it to be concurency proof, it does get more complex.
EDITED added in code to remove used values from #Number, via the OUTPUT caluse of the UPDATE and a DELETE
try using ROW_NUMBER() to join them:
DECLARE #Number table (Value int)
INSERT #Number VALUES (102314)
INSERT #Number VALUES (102315)
INSERT #Number VALUES (102319)
INSERT #Number VALUES (102320)
INSERT #Number VALUES (102324)
INSERT #Number VALUES (102329)
DECLARE #Data table (CustomerId int, PaymentAmt numeric(10,2),ControlNumber int)
INSERT #Data VALUES (1001, 4502.01 ,NULL)
INSERT #Data VALUES (1002, 890.00 ,NULL)
INSERT #Data VALUES (9830, 902923.34 ,NULL)
DECLARE #Used table (Value int)
;WITH RowNumber AS
(
SELECT Value,ROW_NUMBER() OVER(ORDER BY Value) AS RowNumber FROM #Number
)
,RowData AS
(
SELECT CustomerId,ROW_NUMBER() OVER(ORDER BY CustomerId) AS RowNumber, ControlNumber FROM #Data WHERE ControlNumber IS NULL
)
UPDATE d
SET ControlNumber=r.Value
OUTPUT r.Value INTO #Used
FROM RowData d
INNER JOIN RowNumber r ON d.RowNumber=r.RowNumber
DELETE #Number WHERE Value IN (SELECT Value FROM #Used)
SELECT * FROM #Data
SELECT * FROM #Number
OUTPUT:
CustomerId PaymentAmt ControlNumber
----------- --------------------------------------- -------------
1001 4502.01 102314
1002 890.00 102315
9830 902923.34 102319
(3 row(s) affected)
Value
-----------
102320
102324
102329
(3 row(s) affected)
You'll need something to join the two tables together. Some data value that you can match between the two tables.
I'm assuming there's more to your numbers table than just one column of numbers. If there's anything in there that you can match to your data table you can get away with an update.
How are you updating the data table using cursors?

Sql query - how to get when a row first got a certain value

I have a table with rows like this:
ID StatusId Date
1 1 2001-01-01
2 1 2001-01-02
3 2 2001-01-03
4 3 2001-01-04
5 1 2001-01-05
6 2 2001-01-06
7 2 2001-01-07
8 1 2001-01-08
9 1 2001-01-09
I need to get the date when the current value of the status was originally changed. For the above example, the last value is 1, and it's changed in row 8, so the result would be 2001-01-08.
How would you do this?
If you need a table to test with, here it is:
DECLARE #Tbl AS TABLE (ID INT, StatusId INT, Date DATETIME)
INSERT INTO #Tbl(ID, StatusId, Date)
SELECT 1,1,'2001-01-01' UNION
SELECT 2,1,'2001-01-02' UNION
SELECT 3,2,'2001-01-03' UNION
SELECT 4,3,'2001-01-04' UNION
SELECT 5,1,'2001-01-05' UNION
SELECT 6,2,'2001-01-06' UNION
SELECT 7,2,'2001-01-07' UNION
SELECT 8,1,'2001-01-08' UNION
SELECT 9,1,'2001-01-09'
SELECT * FROM #Tbl
This one should get you what you're after:
declare #LastStatusID int
declare #LastDate datetime
declare #LastID int
declare #LastChangeID int
/* get last record */
select top 1 #LastStatusID = StatusID, #LastDate = Date, LastID = ID
from #Tbl
order by ID desc
/* get last record with a different status */
select top 1 #LastChangeID = ID
from #Tbl
where ID < #LastID and StatusID <> #LastStatusID
order by ID desc
/* get the first next record - this would get you the last record as well whe it's just been set */
select top 1 Date
from #Tbl
where ID > #LastChangeID
order by ID asc
I haven't included any checking for margin examples when there'd be just one record in the table or multiple of them but all with the same status. You can figure those out yourself.
As a single query
This query requires IDs without gaps and it will get you the last record after a status change and it will also work when there's just one record in the table or multiple of them with the same status (isnull provides the required functionality)
select top 1 Date
from #tbl t1
left join #tbl t2
on (t2.ID = t1.ID - 1)
where (isnull(t2.StatusID, -1) <> t1.StatusID)
order by ID desc
Last where clause changes a null value (when there's no upper record) to -1. If you do have a status with this value, you should change this number to some non-existing status value.
Something like this:
DECLARE #CurrentID INT, #CurrentDate Date
SELECT TOP 1 #CurrentID = ID, #CurrentDate = Date FROM TABLE
ORDER BY Date DESC
SELECT TOP 1 ID, StatusID, Date
FROM Table
WHERE Date < #CurrentDate
AND ID <> #CurrentID
ORDER BY Date DESC
try
select Date
from #Tbl
where StatusId = (
select StatusId
from #Tbl
order by ID desc limit 1)
order by ID desc
limit 1,1
Please check if your database supports limit or not. If not use equivalent of it (e.g. Top).
I have written this as per mysql.
If the table is guaranteed to have one entry per day (as per your sample data), then the following may work
select MAX(t1.Date)
from
#Tbl t1
inner join
#Tbl t2
on
t1.Date = DATEADD(day,1,t2.Date) and
t1.StatusId <> t2.StatusID
Of course, it's possible to further refine this if there are other columns/criteria, of if the value may never have changed at all. Difficult to tell with the small sample size/output example.
Edit 1 If my one entry per day assumption is wrong, then the from clause can be:
from
#Tbl t1
inner join
#Tbl t2
on
t1.Date > t2.Date and
t1.StatusId <> t2.StatusID
left join
#Tbl t_successive
on
t1.Date > t_successive.Date and
t2.Date < t_successive.Date
where
t_successive.ID is null
(Which uses the left join to ensures rows in t1 and t2 don't have any other rows between them)
This is what I came up with finally:
SELECT T1.ID, T1.StatusId, MIN(T3.Date)
FROM #Tbl T1 INNER JOIN #Tbl T3 ON T1.StatusId = T3.StatusId
WHERE T3.Date > (SELECT MAX(Date) FROM #Tbl T2 WHERE T2.StatusId <> T1.StatusId)
AND T1.ID = (SELECT MAX(ID) FROM #Tbl)
GROUP BY T1.ID, T1.StatusId
and it's doing what I needed it to... thanks everyone

Resources