Poiché stai eliminando il 90% delle righe, ti consiglio di copiare le righe che devi conservare in una nuova tabella con la stessa struttura, quindi utilizzare ALTER TABLE ... SWITCH
per sostituire la tabella esistente con la nuova tabella, quindi semplicemente eliminare la vecchia tabella. Vedi questa pagina di Microsoft Documenti per la sintassi.
Un semplice banco di prova, senza replica che mostra il principio generale:
Innanzitutto, creeremo un database per il nostro test:
USE master;
IF (SELECT 1 FROM sys.databases d WHERE d.name = 'SwitchTest') IS NOT NULL
BEGIN
ALTER DATABASE SwitchTest SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE SwitchTest;
END
CREATE DATABASE SwitchTest;
ALTER DATABASE SwitchTest SET RECOVERY FULL;
BACKUP DATABASE SwitchTest TO DISK = 'NUL:';
GO
Qui, creiamo un paio di tabelle, con un trigger per spostare le righe dalla tabella "A" a "B", approssimando la configurazione.
USE SwitchTest;
GO
CREATE TABLE dbo.A
(
i int NOT NULL
CONSTRAINT PK_A
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
, d varchar(300) NOT NULL
, rowdate datetime NOT NULL
) ON [PRIMARY]
WITH (DATA_COMPRESSION = PAGE);
CREATE TABLE dbo.B
(
i int NOT NULL
CONSTRAINT PK_B
PRIMARY KEY CLUSTERED
, d varchar(300) NOT NULL
, rowdate datetime NOT NULL
) ON [PRIMARY]
WITH (DATA_COMPRESSION = PAGE);
GO
CREATE TRIGGER t_a
ON dbo.A
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DELETE
FROM dbo.B
FROM dbo.B b
INNER JOIN deleted d ON b.i = d.i
INSERT INTO dbo.B (i, d, rowdate)
SELECT i.i
, i.d
, i.rowdate
FROM inserted i;
END
GO
Qui inseriamo 1.000.000 di righe in "A" e, a causa del trigger, anche quelle righe verranno inserite in "B".
;WITH src AS (
SELECT i.n
FROM (VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9))i(n)
)
INSERT INTO dbo.A (d, rowdate)
SELECT d = CRYPT_GEN_RANDOM(300), DATEADD(SECOND, s6.n + (s5.n * 100000) + (s4.n * 10000) + (s3.n * 1000) + (s2.n * 100) + (s1.n * 10), '2017-01-01T00:00:00.000')
FROM src s1
CROSS JOIN src s2
CROSS JOIN src s3
CROSS JOIN src s4
CROSS JOIN src s5
CROSS JOIN src s6;
Cancella il registro delle transazioni, per evitare di rimanere senza spazio. NON ESEGUIRE questo in produzione poiché invia i dati del registro delle transazioni al dispositivo "NUL".
BACKUP LOG SwitchTest TO DISK = 'NUL:';
GO
Questo codice crea una transazione per garantire che nessuna delle tabelle interessate possa essere scritta durante la migrazione di righe:
BEGIN TRANSACTION
EXEC sys.sp_getapplock @Resource = N'TableSwitcher', @LockMode = 'Exclusive', @LockOwner = 'Transaction', @LockTimeout = '1000', @DbPrincipal = N'dbo';
BEGIN TRY
-- create a table to hold the rows we want to keep
CREATE TABLE dbo.C
(
i int NOT NULL
CONSTRAINT PK_C
PRIMARY KEY CLUSTERED
, d varchar(300) NOT NULL
, rowdate datetime NOT NULL
) ON [PRIMARY]
WITH (DATA_COMPRESSION = PAGE);
--copy the rows we want to keep into "C"
INSERT INTO dbo.C (i, d, rowdate)
SELECT b.i
, b.d
, b.rowdate
FROM dbo.B
WHERE b.rowdate >= '2017-01-11T10:00:00';
--truncate the entire "B" table
TRUNCATE TABLE dbo.B;
--"switch" table "C" into "B"
ALTER TABLE dbo.C SWITCH TO dbo.B;
--drop table "C", since we no longer need it
DROP TABLE dbo.C;
--shows the count of rows in "B" which were retained.
SELECT COUNT(1)
FROM dbo.B
WHERE b.rowdate >= '2017-01-11T10:00:00';
--look for rows in "B" that should no longer exist.
SELECT COUNT(1)
FROM dbo.B
WHERE b.rowdate < '2017-01-11T10:00:00';
--release the applock and commit the transaction
EXEC sys.sp_releaseapplock @Resource = N'TableSwitcher', @LockOwner = 'Transaction', @DbPrincipal = N'dbo';
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
DECLARE @message nvarchar(1000) = ERROR_MESSAGE();
DECLARE @severity int = ERROR_SEVERITY();
DECLARE @state int = ERROR_STATE();
RAISERROR (@message, @severity, @state);
EXEC sys.sp_releaseapplock @Resource = N'TableSwitcher', @LockOwner = 'Transaction', @DbPrincipal = N'dbo';
ROLLBACK TRANSACTION;
END CATCH
GO
L' sp_getapplock
e sp_releaseapplock
prevenire più istanze di questo codice in esecuzione allo stesso tempo. Ciò sarebbe utile se si abilita il riutilizzo di questo codice tramite una GUI.
(Si noti che i blocchi delle app sono efficaci solo se ogni processo che accede alla risorsa implementa esplicitamente la stessa logica di blocco manuale delle risorse - non c'è magia che "blocchi" la tabella nello stesso modo in cui SQL Server blocca automaticamente righe, pagine, ecc. Durante un operazione di inserimento / aggiornamento.)
Ora, testiamo il processo di inserimento delle righe in "A", per assicurarci che siano inserite in "B" dal trigger.
INSERT INTO dbo.A (d, rowdate)
VALUES ('testRow', GETDATE());
SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- +
| io | d | riga |
+ --------- + --------- + ------------------------- +
| 1000001 | testRow | 13/04/2018 03: 49: 53.343 |
+ --------- + --------- + ------------------------- +