Ho una grande tabella di dati. Ci sono 10 milioni di record in questa tabella.
Qual è il modo migliore per questa query
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Ho una grande tabella di dati. Ci sono 10 milioni di record in questa tabella.
Qual è il modo migliore per questa query
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Risposte:
Se stai eliminando tutte le righe in quella tabella, l'opzione più semplice è Troncare la tabella, qualcosa del genere
TRUNCATE TABLE LargeTable
GO
La tabella di troncamento svuoterà semplicemente la tabella, non è possibile utilizzare la clausola WHERE per limitare le righe da eliminare e nessun trigger verrà attivato.
D'altra parte, se si sta eliminando più dell'80-90 percento dei dati, dire se si dispone di un totale di 11 milioni di righe e si desidera eliminare 10 milioni in un altro modo sarebbe quello di inserire questi 1 milione di righe (record che si desidera conservare ) a un'altra tabella di gestione temporanea. Tronca questa tabella grande e inserisci di nuovo queste 1 milione di righe.
Oppure se permessi / viste o altri oggetti che hanno questa grande tabella come la loro tabella sottostante non vengono influenzati dalla caduta di questa tabella, puoi ottenere queste quantità relativamente piccole di righe in un'altra tabella, rilasciare questa tabella e creare un'altra tabella con lo stesso schema e importarle righe di nuovo in questa tabella ex-Large.
Un'ultima opzione che mi viene in mente è quella di modificare il database Recovery Mode to SIMPLE
e quindi eliminare le righe in lotti più piccoli utilizzando un ciclo while qualcosa del genere ..
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
e non dimenticarti di ripristinare la modalità di ripristino al massimo e penso che devi eseguire un backup per renderlo pienamente efficace (la modalità di modifica o di ripristino).
optimal solution for unknown case
che è il sogno non è vero? Sfortunatamente non puoi curare ogni malattia con una pillola; Ho suggerito alcune possibili soluzioni per diversi scenari. Purtroppo non esiste un proiettile a nastro.
La risposta di @ m-ali è corretta, ma tieni anche presente che i log potrebbero crescere molto se non esegui il commit della transazione dopo ogni blocco ed esegui un checkpoint. Ecco come lo farei e prendere questo articolo http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes come riferimento, con test delle prestazioni e grafici:
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
e CHECKPOINT
i registri continuano a crescere. Grazie per averlo chiarito.
@Deleted_Rows
con 10000 o potresti finire con un ciclo infinito a causa della sua eliminazione indefinita di piccoli set di dati. Quindi WHILE (@Deleted_Rows = 10000)
- non appena non ci fosse una "pagina" completa di dati per eliminarli, si fermerà. Nell'implementazione WHILE (@Deleted_Rows > 0)
, il ciclo while si eseguirà di nuovo anche se ha eliminato solo una riga e l'esecuzione successiva potrebbe anche trovare una riga o due da eliminare, risultando in un ciclo infinito.
WHILE
ciclo stesso: dateadd(MONTH,-7,GETDATE())
.
WHILE
ciclo.
Puoi anche utilizzare GO + quante volte desideri eseguire la stessa query.
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
dovrebbe funzionare questa sintassi ? Ho un "Impossibile trovare la stored procedure ''" errore. Senza il GO
comando funziona bene però.
@Francisco Goldenstein, solo una piccola correzione. COMMIT deve essere utilizzato dopo aver impostato la variabile, altrimenti WHILE verrà eseguito una sola volta:
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
Questa variazione di M. Ali funziona bene per me. Elimina alcuni, cancella il registro e si ripete. Sto guardando il registro crescere, rilasciare e ricominciare.
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
da eliminare alla volta e anche la WHERE
clausola. Funziona come un fascino!
Se si è disposti (e in grado) di implementare il partizionamento, questa è una tecnica efficace per rimuovere grandi quantità di dati con un sovraccarico di runtime. Tuttavia, non è conveniente per un esercizio una tantum.
Sono stato in grado di eliminare 19 milioni di righe dalla mia tabella di 21 milioni di righe in pochi minuti . Ecco il mio approccio.
Se in questa tabella è presente una chiave primaria a incremento automatico , è possibile utilizzare questa chiave primaria.
Ottieni il valore minimo della chiave primaria della tabella grande in cui readTime <dateadd (MONTH, -7, GETDATE ()). (Aggiungi indice su readTime, se non già presente, questo indice verrà comunque eliminato insieme alla tabella al passaggio 3.). Memorizziamolo in una variabile 'min_primary'
Inserire tutte le righe con chiave primaria> min_primary in una tabella di gestione temporanea (tabella di memoria se il numero di righe non è grande).
Rilascia il grande tavolo.
Ricrea il tavolo. Copia tutte le righe dalla tabella di gestione temporanea alla tabella principale.
Rilasciare la tabella di gestione temporanea.
Puoi eliminare piccoli lotti usando un ciclo while, qualcosa del genere:
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
Un altro uso:
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
Opzionale;
Se il registro delle transazioni è abilitato, disabilitare i registri delle transazioni.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
Se si utilizza SQL Server 2016 o versione successiva e se nella tabella sono state create partizioni basate sulla colonna che si sta tentando di eliminare (ad esempio la colonna Timestamp), è possibile utilizzare questo nuovo comando per eliminare i dati dalle partizioni.
TABELLA TRUNCATE CON (PARTITIONS ({|} [, ... n]))
Ciò eliminerà i dati solo nelle partizioni selezionate e dovrebbe essere il modo più efficiente per eliminare i dati da una parte della tabella poiché non creerà i registri delle transazioni e verrà eseguito con la stessa velocità del normale troncamento, ma senza eliminare tutti i dati dal tavolo.
Lo svantaggio è se la tua tabella non è configurata con la partizione, quindi devi andare alla vecchia scuola ed eliminare i dati con un approccio regolare e quindi ricreare la tabella con le partizioni in modo da poterlo fare in futuro, che è quello che ho fatto. Ho aggiunto la creazione e la cancellazione della partizione nella stessa procedura di inserimento. Avevo una tabella con 500 milioni di righe, quindi questa era l'unica opzione per ridurre i tempi di cancellazione.
Per maggiori dettagli, consultare i collegamenti seguenti: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL Server 2016 Troncare la tabella con le partizioni
Di seguito è riportato ciò che ho fatto per eliminare i dati prima di poter ricreare la tabella con le partizioni con i dati richiesti al suo interno. Questa query verrà eseguita per giorni durante l'intervallo di tempo specificato fino a quando i dati non vengono eliminati.
: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()
Se dico senza loop, posso usare la GOTO
dichiarazione per cancellare grandi quantità di record usando il server SQL. exa.
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
in questo modo è possibile eliminare grandi quantità di dati con dimensioni di eliminazione inferiori.
fatemi sapere se richiede maggiori informazioni.