it-swarm.it

Metodi per accelerare un enorme DELETE FROM <table> senza clausole

Utilizzando SQL Server 2005.

Sto eseguendo un enorme DELETE FROM senza clausole where. È sostanzialmente equivalente a un'istruzione TRUNCATE TABLE - tranne per il fatto che non mi è permesso usare TRUNCATE. Il problema è che la tabella è enorme: 10 milioni di righe e il completamento richiede più di un'ora. Esiste un modo per renderlo più veloce senza:

  • Utilizzo di Troncare
  • Disabilitare o eliminare gli indici?

Il t-log è già su un disco separato.

Eventuali suggerimenti sono benvenuti!

37
tuseau

Quello che puoi fare è eliminare in batch in questo modo:

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable

Dove xxx è, diciamo, 50000

Una modifica di questo, se si desidera rimuovere una percentuale molto alta di righe ...

SELECT col1, col2, ... INTO #Holdingtable
           FROM MyTable WHERE ..some condition..

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (xxx) MyTable WHERE ...

INSERT MyTable (col1, col2, ...)
           SELECT col1, col2, ... FROM #Holdingtable
39
gbn

Puoi usare la clausola TOP per farlo facilmente:

WHILE (1=1)
BEGIN
    DELETE TOP(1000) FROM table
    IF @@ROWCOUNT < 1 BREAK
END
21
SQLRockstar

Sono d'accordo con i suggerimenti per raggruppare le tue eliminazioni in blocchi gestibili se non riesci a utilizzare TRUNCATE e mi piace il suggerimento di rilascio/creazione per la sua originalità, ma sono curioso di sapere il seguente commento nella tua domanda:

È sostanzialmente equivalente a un'istruzione TRUNCATE TABLE - tranne che non mi è permesso usare TRUNCATE

Immagino che il motivo di questa restrizione abbia a che fare con la sicurezza che deve essere concessa per troncare direttamente una tabella e il fatto che ti consentirebbe di troncare tabelle diverse da quella di cui ti occupi.

Supponendo che sia così, mi chiedo se la creazione di una procedura memorizzata che utilizza TRUNCATE TABLE e che utilizza "EXECUTE AS" sia considerata una valida alternativa ai diritti di sicurezza necessari per troncare la tabella direttamente.

Speriamo che questo ti dia la velocità di cui hai bisogno, affrontando anche i problemi di sicurezza che la tua azienda potrebbe avere con l'aggiunta del tuo account al ruolo db_ddladmin.

Un altro vantaggio dell'utilizzo di una procedura memorizzata in questo modo è che la procedura memorizzata stessa potrebbe essere bloccata in modo che solo gli account specifici possano utilizzarla.

Se per qualche motivo questa non è una soluzione accettabile e la necessità di rimuovere i dati in questa tabella è qualcosa che deve essere fatto una volta al giorno/ora/ecc., Chiederei che sia stato creato un processo di SQL Agent per troncare la tabella ogni giorno a un'ora prestabilita.

Spero che sia di aiuto!

7
Jeff

Tranne il troncamento .. solo l'eliminazione in batch può aiutarti.

È possibile eliminare la tabella e ricrearla, con tutti i vincoli e gli indici, fuori rotta. In Management Studio hai la possibilità di scrivere una tabella da eliminare e creare, quindi dovrebbe essere un'opzione banale. Ma questo solo se ti è permesso fare azioni DDL, che vedo non è davvero un'opzione.

5
Marian

Poiché questa domanda è un riferimento così importante, sto pubblicando questo codice che mi ha davvero aiutato a capire l'eliminazione con i loop e anche i messaggi all'interno di un loop per tenere traccia dei progressi.

La query viene modificata da this domanda duplicata. Accredita a @ RLF per la base di query.

CREATE TABLE #DelTest (ID INT IDENTITY, name NVARCHAR(128)); -- Build the test table
INSERT INTO #DelTest (name) SELECT name FROM sys.objects;  -- fill from system DB
SELECT COUNT(*) TableNamesContainingSys FROM #deltest WHERE name LIKE '%sys%'; -- check rowcount
go
DECLARE @HowMany INT;
DECLARE @RowsTouched INT;
DECLARE @TotalRowCount INT;
DECLARE @msg VARCHAR(100);
DECLARE @starttime DATETIME 
DECLARE @currenttime DATETIME 

SET @RowsTouched = 1; -- Needs to be >0 for loop to start
SET @TotalRowCount=0  -- Total rows deleted so far is 0
SET @HowMany = 5;     -- Variable to choose how many rows to delete per loop
SET @starttime=GETDATE()

WHILE @RowsTouched > 0
BEGIN
   DELETE TOP (@HowMany)
   FROM #DelTest 
   WHERE name LIKE '%sys%';

   SET @RowsTouched = @@ROWCOUNT; -- Rows deleted this loop
   SET @TotalRowCount = @[email protected]; -- Increment Total rows deleted count
   SET @currenttime = GETDATE();
   SELECT @msg='Deleted ' + CONVERT(VARCHAR(9),@TotalRowCount) + ' Records. Runtime so far is '+CONVERT(VARCHAR(30),DATEDIFF(MILLISECOND,@starttime,@currenttime))+' milliseconds.'
   RAISERROR(@msg, 0, 1) WITH NOWAIT;  -- Print message after every loop. Can't use the PRINT function as SQL buffers output in loops.  

END; 
SELECT COUNT(*) TableNamesContainingSys FROM #DelTest WHERE name LIKE '%sys%'; -- Check row count after loop finish
DROP TABLE #DelTest;
1
Max xaM