Vì bạn đang xóa 90% các hàng, tôi khuyên bạn nên sao chép các hàng bạn cần giữ vào một bảng mới có cùng cấu trúc, sau đó sử dụng ALTER TABLE ... SWITCH
để thay thế bảng hiện có bằng bảng mới, sau đó chỉ cần bỏ bảng cũ. Xem trang Microsoft Docs này để biết cú pháp.
Một giường thử nghiệm đơn giản, không có bản sao cho thấy nguyên tắc chung:
Trước tiên, chúng tôi sẽ tạo cơ sở dữ liệu cho thử nghiệm của chúng tôi:
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
Ở đây, chúng tôi tạo một vài bảng, với một bộ kích hoạt để di chuyển các hàng từ bảng "A" sang "B", gần đúng với thiết lập của bạn.
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
Ở đây, chúng tôi chèn 1.000.000 hàng vào "A" và vì kích hoạt, những hàng đó cũng sẽ được chèn vào "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;
Xóa nhật ký giao dịch, để tránh hết phòng. KHÔNG CHẠY cái này trong sản xuất vì nó sẽ gửi dữ liệu nhật ký giao dịch đến thiết bị "NUL".
BACKUP LOG SwitchTest TO DISK = 'NUL:';
GO
Mã này tạo ra một giao dịch để đảm bảo không có bảng nào bị ảnh hưởng có thể được ghi trong khi chúng tôi đang di chuyển các hàng:
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
Các sp_getapplock
và sp_releaseapplock
ngăn chặn nhiều trường hợp của mã này chạy cùng một lúc. Điều này sẽ hữu ích nếu bạn cho phép mã này được sử dụng lại thông qua GUI.
(Lưu ý rằng khóa ứng dụng chỉ hiệu quả nếu mọi quá trình truy cập vào tài nguyên cụ tương tự của nhãn hiệu tài nguyên khóa luận một cách rõ ràng - không có kỳ diệu mà "khóa" các bảng trong cùng một cách mà SQL Server sẽ tự động khóa hàng, các trang, vv trong một thao tác chèn / cập nhật.)
Bây giờ, chúng tôi kiểm tra quá trình chèn các hàng vào "A", để đảm bảo chúng được chèn vào "B" bằng trình kích hoạt.
INSERT INTO dbo.A (d, rowdate)
VALUES ('testRow', GETDATE());
SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- +
| tôi | d | chèo thuyền |
+ --------- + --------- + ------------------------- +
| 1000001 | kiểm tra | 2018-04-13 03: 49: 53.343 |
+ --------- + --------- + ------------------------- +