I think Michael's answer really should be more efficient. Evaluating the effectiveness, I just want to pay attention to the problem with the cost of the request (relative to the package) shown in Management Studio.
I set up a test table with 23174 rows and fulfilled the query in the question and Michael. Considering the โrequest cost (relative to the batch)โ in the actual execution plan, the initial request was 1%, and Michael's cost was 99%, and therefore it seems to be ineffective.

However, actual statistics tell a completely different story.
Intersection Approach
SQL Server Runtime: CPU time = 0 ms, elapsed time = 0 ms.
The table is "MyTable". Scan number 23175 , logical read 49335 , physical read 0, read-ahead read 0, logical read lob 0, physical read lob 0, lob read-ahead read 0.
approach ROW_NUMBER
SQL Server Runtime: CPU time = 391 ms, elapsed time = 417 ms.
Table "Desktop". Number of scans 0, logical read 0, physical read 0, read-ahead reads 0, logical reads lob 0, physical reads lob 0, lob read-ahead reads 0.
The table is "MyTable". Scan number 2, logical readings 148, physical read 0, read forward 0, logical read forehead 0, physical read 0, read read 0.
In the ROW_NUMBER plan ROW_NUMBER Merge Join on rownumber=rownumber+1 has 23174 rows going in both directions. This value is unique, and the actual rows are 23.174. However, SQL Server estimates that the rows created from this connection will be 34,812,000, and therefore its estimated cost for insertion later in the plan is wildly inaccurate.
Script test
BEGIN TRAN CREATE TABLE MyTable ( [ObjectID] [INT] IDENTITY(1, 1) NOT NULL PRIMARY KEY CLUSTERED, [FileName] [VARCHAR](50) NULL, [CreatedDate] [DATETIME] NULL ) GO INSERT INTO MyTable SELECT ISNULL(type, NEWID()), DATEADD(DAY, CAST(RAND(CAST(NEWID() AS VARBINARY)) * 10000 AS INT), GETDATE()) FROM master.dbo.spt_values, (SELECT TOP 10 1 AS X FROM master.dbo.spt_values) V DELETE FROM MyTable WHERE EXISTS(SELECT * FROM MyTable m2 WHERE MyTable.CreatedDate = m2.CreatedDate AND MyTable.FileName = m2.FileName AND MyTable.ObjectID < m2.ObjectID) CREATE UNIQUE NONCLUSTERED INDEX [IX_MyTable] ON MyTable ([FileName] ASC, [CreatedDate] ASC) SET STATISTICS IO ON SET STATISTICS TIME ON SELECT A.ObjectID, A.FileName, A.CreatedDate AS CreatedDate, B.PrevRowCreatedDate, DATEDIFF("SS", '1900-01-01 00:00:00', COALESCE(( A.CreatedDate - B.PrevRowCreatedDate ), 0)) AS secondsTaken INTO