큰 데이터 테이블이 있습니다. 이 테이블에는 1000 만 개의 레코드가 있습니다.
이 쿼리에 대한 가장 좋은 방법은 무엇입니까?
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
큰 데이터 테이블이 있습니다. 이 테이블에는 1000 만 개의 레코드가 있습니다.
이 쿼리에 대한 가장 좋은 방법은 무엇입니까?
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
답변:
해당 테이블의 모든 행을 삭제하는 경우 가장 간단한 옵션은 테이블을 자르는 것입니다.
TRUNCATE TABLE LargeTable
GO
Truncate table은 단순히 테이블을 비우고 WHERE 절을 사용하여 삭제되는 행을 제한 할 수 없으며 트리거가 실행되지 않습니다.
반면에 데이터의 80-90 % 이상을 삭제하는 경우 총 1 천 1 백만 행이 있고 1 천만 행을 삭제하려는 경우 다른 방법은이 1 백만 행을 삽입하는 것입니다 (보관하려는 레코드 )을 다른 스테이징 테이블에 추가합니다. 이 대형 테이블을 자르고이 100 만 행을 다시 삽입하십시오.
또는이 테이블을 기본 테이블로 사용하는 권한 /보기 또는 기타 개체가이 테이블을 삭제해도 영향을받지 않는 경우 상대적으로 적은 양의 행을 다른 테이블로 가져 와서이 테이블을 삭제하고 동일한 스키마를 가진 다른 테이블을 만들고 가져올 수 있습니다. 이 ex-Large 테이블에 행을 다시 넣습니다.
내가 생각할 수있는 마지막 옵션은 데이터베이스를 변경 Recovery Mode to SIMPLE
한 다음 이와 같은 while 루프를 사용하여 더 작은 일괄 처리로 행을 삭제하는 것입니다.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
END
그리고 복구 모드를 다시 전체로 변경하는 것을 잊지 마십시오. 완전히 적용되도록하려면 백업을 수행해야한다고 생각합니다 (변경 또는 복구 모드).
optimal solution for unknown case
그게 꿈이지 않습니까? 안타깝게도 한 알약으로 모든 질병을 치료할 수는 없습니다. 여러 시나리오에 대해 몇 가지 가능한 솔루션을 제안했습니다. 불행히도 여기에는 은색 총알이 없습니다.
@ m-ali 대답은 맞지만 각 청크 후에 트랜잭션을 커밋하지 않고 체크 포인트를 수행하면 로그가 많이 커질 수 있음을 명심하십시오. 이것이 내가 그것을하는 방법 이며 성능 테스트 및 그래프와 함께 http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes 를 참조로 사용합니다.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
COMMIT TRANSACTION
그리고 CHECKPOINT
통나무는 여전히 성장하고 있습니다. 이것을 명확히 해주셔서 감사합니다.
@Deleted_Rows
10000과 비교하고 싶 거나 작은 데이터 세트를 무기한 삭제하기 때문에 무한 루프가 발생할 수 있습니다. 따라서 WHILE (@Deleted_Rows = 10000)
-삭제할 데이터의 전체 "페이지"가 없으면 즉시 중지됩니다. 구현 WHILE (@Deleted_Rows > 0)
에서 while 루프는 한 행만 삭제하더라도 다시 실행되며 다음 실행에서도 삭제할 행 또는 두 개를 찾을 수 있습니다. 결과적으로 무한 루프가 발생합니다.
WHILE
루프 자체 내에서 반복적으로 날짜를 계산하기 때문에 각 반복마다 다릅니다 dateadd(MONTH,-7,GETDATE())
..
WHILE
루프의 다른 반복 사이에서 삭제 될 수있는 새 레코드가 생성 될 수 있습니다 .
GO + 동일한 쿼리를 실행하려는 횟수를 사용할 수도 있습니다.
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
GO xx
에서 작동합니까? 내가 얻을 "저장 프로 시저를 찾을 수 없습니다 '" 오류가 발생했습니다. GO
명령 없이는 잘 작동합니다.
@Francisco Goldenstein, 사소한 수정입니다. COMMIT는 변수를 설정 한 후에 사용해야합니다. 그렇지 않으면 WHILE이 한 번만 실행됩니다.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
M.Ali 의이 변형은 저에게 잘 작동합니다. 일부를 삭제하고 로그를 지우고 반복합니다. 나는 로그가 커지고, 떨어지고, 다시 시작하는 것을보고 있습니다.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
SET @Deleted_Rows = @@ROWCOUNT;
dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END
# of rows
한 번에 삭제할 을 매개 변수화 하고 WHERE
절도 수정했습니다 . 매력처럼 작동합니다!
2 천 1 백만 행의 테이블에서 몇 분 만에 1 천 9 백만 행을 삭제할 수있었습니다 . 여기 내 접근 방식이 있습니다.
당신이있는 경우 자동 증가 기본 키 이 테이블을, 당신은이 기본 키를 사용할 수있다.
readTime <dateadd (MONTH, -7, GETDATE ()) 인 대형 테이블의 기본 키 최소값을 가져옵니다. (readTime에 인덱스를 추가합니다. 아직없는 경우이 인덱스는 3 단계의 테이블과 함께 삭제됩니다.) 변수 'min_primary'에 저장할 수 있습니다.
기본 키> min_primary가있는 모든 행을 준비 테이블 (행 수가 크지 않은 경우 메모리 테이블)에 삽입합니다.
큰 테이블을 삭제하십시오.
테이블을 다시 만듭니다. 준비 테이블의 모든 행을 기본 테이블로 복사합니다.
스테이징 테이블을 삭제하십시오.
또 다른 용도 :
SET ROWCOUNT 1000 -- Buffer
DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())
DELETE LargeTable WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
DELETE LargeTable WHERE readTime < @DATE
END
SET ROWCOUNT 0
선택 과목;
트랜잭션 로그가 활성화 된 경우 트랜잭션 로그를 비활성화합니다.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
SQL Server 2016 이상을 사용하고 테이블에 삭제하려는 열 (예 : Timestamp 열)을 기반으로 생성 된 파티션이있는 경우이 새 명령을 사용하여 파티션별로 데이터를 삭제할 수 있습니다.
(파티션 ({|} [, ... n]))으로 테이블 자르기
이렇게하면 선택한 파티션의 데이터 만 삭제되며 트랜잭션 로그가 생성되지 않고 모든 데이터가 삭제되지 않고 일반 자르기만큼 빠르게 수행되므로 테이블의 일부에서 데이터를 삭제하는 가장 효율적인 방법입니다. 테이블에서.
단점은 테이블이 파티션으로 설정되지 않은 경우 구식으로 가서 정기적 인 접근 방식으로 데이터를 삭제 한 다음 나중에이를 수행 할 수 있도록 파티션이있는 테이블을 다시 만들어야한다는 것입니다. 삽입 절차 자체에 파티션 생성 및 삭제를 추가했습니다. 5 억 개의 행이있는 테이블이 있었기 때문에 이것이 삭제 시간을 줄이는 유일한 옵션이었습니다.
자세한 내용은 아래 링크를 참조하세요. https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL Server 2016 파티션이있는 테이블 자르기
다음은 필요한 데이터가있는 파티션이있는 테이블을 다시 생성하기 전에 먼저 데이터를 삭제하기 위해 수행 한 작업입니다. 이 쿼리는 데이터가 삭제 될 때까지 지정된 기간 동안 며칠 동안 실행됩니다.
:connect <<ServerName>>
use <<DatabaseName>>
SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate = getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;
/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT
WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT
WHILE (1=1)
BEGIN
WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (500000) dbo.<<table_name>>
WHERE timestamp_column < convert(datetime, @FlagDate,102)
SET @Deleted_Rows = @@ROWCOUNT;
WAITFOR DELAY '00:00:01'
select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
set @loopnum = @loopnum + 1
if @loopnum > 1000
begin
begin try
DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
RAISERROR( @msg ,0,1) WITH NOWAIT
end try
begin catch
RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT
end catch
set @loopnum = 1
end
END
WAITFOR DELAY '00:10:00'
END
select getdate()