;WITH CTE AS(SELECT ROW_NUMBER() OVER(PARTITION BY SIZE ORDER BY ID) RN , ID, SIZE FROM @table)DELETE FROM CTE WHERE RN <> 1
-- Create a test databaseUSE master;CREATE DATABASE RemoveDuplicates;GOUSE RemoveDuplicates;GO-- Source table with duplicate data (must reside on the default file group)CREATE TABLE dbo.Data ( id INTEGER IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED, size INTEGER NOT NULL ) ON [DEFAULT];GO-- Sample dataINSERT dbo.Data (size)VALUES (1), (1), (2), (3), (2), (1), (3);GO-- Sample non-clustered indexCREATE NONCLUSTERED INDEX [IX dbo.Data size]ON dbo.Data (size)ON [DEFAULT];GO-- Identify the records to keep and save them to a new tableSELECT D1.id, D1.sizeINTO dbo.ToSaveFROM ( SELECT *, rn = ROW_NUMBER() OVER (PARTITION BY size ORDER BY id) FROM dbo.Data D1 ) D1WHERE D1.rn = 1;GO-- Switch to the bulk logged recovery model to allow-- minimally-logged index builds-- (in a real system you would take a log backup now)ALTER DATABASE RemoveDuplicatesSET RECOVERY BULK_LOGGED;GO-- The new table needs the same indexes as the source table-- Full requirements: http://technet.microsoft.com/en-us/library/ms191160.aspxALTER TABLE dbo.ToSaveADD PRIMARY KEY CLUSTERED (id);GOCREATE INDEX nc1 ON dbo.ToSave (size) ON [DEFAULT];GO-- Switch back to full recovery-- (in a real system you would take a second log backup now)ALTER DATABASE RemoveDuplicatesSET RECOVERY FULL;GO-- Replace the original table with the saved dataBEGIN TRY -- Begin atomic operation BEGIN TRANSACTION; -- Empty the source table TRUNCATE TABLE dbo.Data; -- Transfer the saved rows (metadata operation) ALTER TABLE dbo.ToSave SWITCH TO dbo.Data; -- Drop the temporary storage DROP TABLE dbo.ToSave; -- Success COMMIT TRANSACTION;END TRYBEGIN CATCH -- Error handling DECLARE @Msg NVARCHAR(2048); SET @Msg = ERROR_MESSAGE(); IF XACT_STATE() <> 0 ROLLBACK TRANSACTION; RAISERROR('ROLLBACK: %s', 16, 1, @Msg);END CATCH;GO-- Show the de-duplicated dataSELECT *FROM dbo.Data;GO-- Tidy upUSE master;DROP DATABASE RemoveDuplicates;