Tengo una gran tabla de datos. Hay 10 millones de registros en esta tabla.
¿Cuál es la mejor manera para esta consulta?
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Tengo una gran tabla de datos. Hay 10 millones de registros en esta tabla.
¿Cuál es la mejor manera para esta consulta?
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Respuestas:
Si está eliminando todas las filas de esa tabla, la opción más simple es Truncar la tabla, algo así como
TRUNCATE TABLE LargeTable
GO
Truncar la tabla simplemente vaciará la tabla, no puede usar la cláusula WHERE para limitar las filas que se eliminarán y no se dispararán desencadenantes.
Por otro lado, si está eliminando más del 80-90 por ciento de los datos, diga que si tiene un total de 11 millones de filas y desea eliminar 10 millones, otra forma sería insertar estos 1 millón de filas (registros que desea mantener ) a otra tabla de etapas. Trunca esta tabla grande e inserta de nuevo estas 1 millón de filas.
O si los permisos / vistas u otros objetos que tienen esta tabla grande como su tabla subyacente no se ven afectados al soltar esta tabla, puede obtener esta cantidad relativamente pequeña de las filas en otra tabla, soltar esta tabla y crear otra tabla con el mismo esquema e importarlos filas de nuevo en esta tabla ex-Large.
Una última opción que se me ocurre es cambiar la base de datos Recovery Mode to SIMPLE
y luego eliminar filas en lotes más pequeños usando un ciclo while algo como esto ...
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
y no olvide volver a cambiar el modo de Recuperación a completo y creo que debe hacer una copia de seguridad para que sea completamente efectivo (los modos de cambio o recuperación).
optimal solution for unknown case
ese es el sueño, ¿no? Lamentablemente, no puede curar todas las enfermedades con una sola píldora; He sugerido algunas posibles soluciones para diferentes escenarios. Desafortunadamente, no hay una bala de astilla aquí.
La respuesta de @ m-ali es correcta, pero también tenga en cuenta que los registros podrían crecer mucho si no confirma la transacción después de cada fragmento y realiza un punto de control. Así es como lo haría y tomaría este artículo http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes como referencia, con pruebas de rendimiento y gráficos:
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
y CHECKPOINT
los registros siguen creciendo. Gracias por dejar esto claro.
@Deleted_Rows
con 10000 o puede terminar con un bucle infinito debido a que elimina indefinidamente pequeños conjuntos de datos. Entonces WHILE (@Deleted_Rows = 10000)
, tan pronto como no haya una "página" completa de datos para eliminar, se detendrá. En su implementación, WHILE (@Deleted_Rows > 0)
el ciclo while se ejecutará nuevamente incluso si solo eliminó una fila, y la próxima ejecución también podría encontrar una o dos filas para eliminar, lo que da como resultado un ciclo infinito.
WHILE
propio bucle: dateadd(MONTH,-7,GETDATE())
.
WHILE
bucle.
También puede usar GO + cuántas veces desea ejecutar la misma consulta.
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
supone que esta sintaxis funciona? Me sale un "no se encontró el procedimiento almacenado ''" error. Sin GO
embargo, sin el comando funciona bien.
@Francisco Goldenstein, solo una pequeña corrección. El COMMIT debe usarse después de establecer la variable; de lo contrario, WHILE se ejecutará solo una vez:
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
Esta variación de M. Ali está funcionando bien para mí. Elimina algunos, borra el registro y se repite. Estoy viendo el registro crecer, caer y comenzar de nuevo.
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
eliminación a la vez, y también la WHERE
cláusula. ¡Funciona de maravilla!
Si está dispuesto (y puede) implementar la partición, esa es una técnica efectiva para eliminar grandes cantidades de datos con poca sobrecarga de tiempo de ejecución. Sin embargo, no es rentable para un ejercicio único.
Pude eliminar 19 millones de filas de mi tabla de 21 millones de filas en cuestión de minutos . Aquí está mi enfoque.
Si tiene una clave primaria de incremento automático en esta tabla, puede utilizar esta clave primaria.
Obtenga el valor mínimo de la clave primaria de la tabla grande donde readTime <dateadd (MONTH, -7, GETDATE ()). (Agregue índice en readTime, si aún no está presente, este índice se eliminará de todos modos junto con la tabla en el paso 3.). Vamos a almacenarlo en una variable 'min_primary'
Inserte todas las filas que tienen la clave primaria> min_primary en una tabla de preparación (tabla de memoria si el número de filas no es grande).
Suelta la mesa grande.
Recrea la mesa. Copie todas las filas de la tabla de etapas a la tabla principal.
Suelta la mesa de ensayo.
Puede eliminar lotes pequeños usando un ciclo while, algo como esto:
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
Otro 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
Opcional;
Si el registro de transacciones está habilitado, deshabilite los registros de transacciones.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
Si está utilizando SQL Server 2016 o superior y si su tabla tiene particiones creadas en función de la columna que está tratando de eliminar (por ejemplo, la columna Timestamp), puede usar este nuevo comando para eliminar datos por particiones.
TABLA TRUNCADA CON (PARTICIONES ({|} [, ... n]))
Esto eliminará los datos solo en la (s) partición (es) seleccionada (s) y debería ser la forma más eficiente de eliminar datos de una parte de la tabla, ya que no creará registros de transacciones y se realizará tan rápido como el truncamiento normal pero sin que se eliminen todos los datos. de la mesa.
El inconveniente es que si su tabla no está configurada con partición, entonces necesita ir a la vieja escuela y eliminar los datos con un enfoque regular y luego volver a crear la tabla con particiones para que pueda hacer esto en el futuro, que es lo que hice. Agregué la creación y eliminación de particiones en el procedimiento de inserción. Tenía una tabla con 500 millones de filas, por lo que esta era la única opción para reducir el tiempo de eliminación.
Para obtener más detalles, consulte los siguientes enlaces: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
Tabla truncada de SQL Server 2016 con particiones
A continuación se muestra lo que hice primero para eliminar los datos antes de poder volver a crear la tabla con particiones con los datos requeridos. Esta consulta se ejecutará durante días durante la ventana de tiempo especificada hasta que se eliminen los datos.
: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()
Si digo sin bucle, puedo usar la GOTO
instrucción para eliminar una gran cantidad de registros usando el servidor SQL. exa.
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
De esta manera, puede eliminar una gran cantidad de datos con un tamaño de eliminación más pequeño.
avíseme si requiere más información.