행의 90 %를 삭제하므로 동일한 구조의 새 테이블에 보관해야하는 행을 복사 한 다음 ALTER TABLE ... SWITCH
기존 테이블을 새 테이블로 바꾸고 이전 테이블을 삭제하는 것이 좋습니다. 구문 은 이 Microsoft 문서 페이지 를 참조하십시오 .
복제가없는 간단한 테스트 베드로 일반적인 원칙을 보여줍니다.
먼저 테스트를위한 데이터베이스를 만듭니다.
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
여기에서는 행을 테이블 "A"에서 "B"로 이동하는 트리거를 사용하여 설정에 가까운 두 개의 테이블을 만듭니다.
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
여기에서는 1,000,000 개의 행을 "A"에 삽입하고 트리거로 인해 해당 행도 "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;
공간이 부족하지 않도록 트랜잭션 로그를 지우십시오. 트랜잭션 로그 데이터를 "NUL"장치로 전송하기 때문에 프로덕션 환경에서는 이것을 실행하지 마십시오.
BACKUP LOG SwitchTest TO DISK = 'NUL:';
GO
이 코드는 행을 마이그레이션하는 동안 영향을받는 테이블을 쓸 수 없도록 트랜잭션을 만듭니다.
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
sp_getapplock
과 sp_releaseapplock
동시에 실행이 코드의 여러 인스턴스를 방지 할 수 있습니다. GUI를 통해이 코드를 재사용 할 수 있도록하는 경우 유용합니다.
응용 프로그램 잠금은 리소스에 액세스하는 모든 프로세스가 동일한 수동 리소스 잠금 논리를 명시 적으로 구현하는 경우에만 유효합니다 . 삽입 / 업데이트 작업)
이제 트리거에 의해 행이 "B"에 삽입되도록 "A"에 행을 삽입하는 프로세스를 테스트합니다.
INSERT INTO dbo.A (d, rowdate)
VALUES ('testRow', GETDATE());
SELECT *
FROM dbo.B
WHERE B.d = 'testRow'
+ --------- + --------- + ------------------------- +
| 나는 | d | rowdate |
+ --------- + --------- + ------------------------- +
| 1000001 | testRow | 2018-04-13 03 : 49 : 53.343 |
+ --------- + --------- + ------------------------- +