2012-06-27 17 views
20

Digamos que tenemos la tabla Ventas con 30 columnas y 500,000 filas. Me gustaría eliminar 400,000 en la tabla (aquellos donde "toDelete='1'").Cómo eliminar filas de manera eficiente mientras NO se usa Truncar tabla en una tabla de más de 500,000 filas

Pero tengo unas pocas restricciones:

  • la tabla se lee/escribe "a menudo" y no me gustaría mucho "eliminar" a tomar mucho tiempo y bloquear la tabla durante demasiado tiempo
  • tengo que saltar el registro de transacciones (como con un TRUNCATE), pero mientras se hace un "DELETE ... WHERE..." (tengo que poner una condición), pero no he encontrado ninguna manera de hacer esto ...

cualquier consejo ser bienvenido a transformar un

DELETE FROM Sales WHERE toDelete='1' 

a algo más particionado & posiblemente registro de transacciones gratis.

+2

¿Por qué necesita para omitir el registro de transacciones? –

+1

Le agradecería que, una vez que haya terminado, publique la solución óptima (o al menos la que mejor funcionó para usted). –

+0

@ thecoon: definitivamente lo haré. Gracias a ALL por sus diversas respuestas complementarias. –

Respuesta

5

Una forma en que tuve que hacer esto en el pasado es tener un procedimiento almacenado o una secuencia de comandos que borre n registros. Repita hasta que termine.

DELETE TOP 1000 FROM Sales WHERE toDelete='1' 
+2

¡Cada vez que encoges una base de datos, un gatito muere! [http://www.sqlskills.com/BLOGS/PAUL/post/Why-you-should-not-shrink-your-data-files.aspx](http://www.sqlskills.com/BLOGS/PAUL/ post/Why-you-should-not-shrink-your-data-files.aspx) –

+0

Para aclarar (ya que es un poco un hack), estaba haciendo esto debido a las limitaciones de espacio de disco en mi directorio de registro en el momento , en lugar de bloquear. Supongo que la duración bloqueada con esto es la misma, simplemente extenderse más tiempo :) – Cylindric

+0

¿Reducir la base de datos? No, por favor no hagas eso. Espero que quisieras decir punto de control o algo ... –

1

Usted debe tratar de darle un toque ROWLOCK por lo que no se bloqueará toda la tabla. Sin embargo, si elimina muchas filas, se producirá una escalada de bloqueo.

Además, asegúrese de tener un índice no agrupado (solo para 1 valores) en la columna toDelete. Si es posible, conviértalo en una columna de bit, no varchar (o lo que es ahora).

DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1' 

En última instancia, puede tratar de iterar sobre la tabla y eliminar en trozos.

Actualizado

Desde mientras bucles y eliminaciones trozo son el nuevo rosa aquí, voy a tirar en mi versión demasiado (combinado con mi respuesta anterior):

SET ROWCOUNT 100 
DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1' 

WHILE @@rowcount > 0 
BEGIN 
    SET ROWCOUNT 100 
    DELETE FROM Sales WITH(ROWLOCK) WHERE toDelete='1' 
END 
+2

' SET ROWCOUNT' está [en desuso en SQL 2012] (http://msdn.microsoft.com/en-us/library/ms143729.aspx). – ErikE

27

Calling DELETE FROM TableName hará la eliminación completa en una transacción grande. Esto es caro.

Aquí es otra opción que será eliminar filas en lotes:

deleteMore: 
DELETE TOP(10000) Sales WHERE toDelete='1' 
IF @@ROWCOUNT != 0 
    goto deleteMore 
+2

¡Oh! ¿Cómo nunca me di cuenta? que puedes poner una expresión 'TOP()' en una declaración 'DELETE'? ¡Mucho más conciso que mi respuesta funcionalmente idéntica! +1 a ti señor! – MatBailie

+8

Francamente, ni siquiera recuerdo que pudieras usar etiquetas en SQL 2008. I preferiría ver una instrucción WHILE. "WHILE 1 = 1 BEGIN; DELETE ...; IF @@ RowCount = 0 BREAK; END;' Esto para mí es más claro para el siguiente escritor de sql que aparece que se está produciendo un bucle, en lugar de descubrir el horrible GOTO. – ErikE

10

Lo que queremos es el procesamiento por lotes.

While (select Count(*) from sales where toDelete =1) >0 
BEGIN 
Delete from sales where SalesID in 
(select top 1000 salesId from sales where toDelete = 1) 
END 

Por supuesto, puede experimentar que es el mejor valor a utilizar para el lote, que he usado desde 500 hasta 50.000 dependiendo de la tabla. Si usa la eliminación en cascada, probablemente necesite un número más pequeño ya que tiene esos registros secundarios para eliminar.

+0

Las llamadas repetidas a 'COUNT (*)' parecen innecesarias cuando se puede usar '@@ rowcount'. Pero es una solución muy portátil para otros RDBMS ... – MatBailie

+1

Es cierto, yo aceleró esto rápidamente y no tardó en optimizar completamente. Y realmente no estoy acostumbrado a eliminar el tener palabra clave superior, difícil de contrastar con años de práctica a veces. – HLGEM

+0

Acaba de eliminar el: en la última etiqueta deleteMore porque me dio una etiqueta de error que ya fue declarada. Funcionó bien con esta pequeña corrección. –

0

Mi propia opinión sobre esta funcionalidad sería la siguiente. De esta manera no hay código repetido y puede administrar el tamaño de su porción.

DECLARE @DeleteChunk INT = 10000 
DECLARE @rowcount INT = 1 

WHILE @rowcount > 0 
BEGIN 

    DELETE TOP (@DeleteChunk) FROM Sales WITH(ROWLOCK) 

    SELECT @rowcount = @@RowCount 
END 
0

He utilizado el siguiente para eliminar alrededor de 50 millones de discos -

BEGIN TRANSACTION  
    DeleteOperation: 
    DELETE TOP (BatchSize) 
    FROM [database_name].[database_schema].[database_table] 

    IF @@ROWCOUNT > 0 
    GOTO DeleteOperation 
COMMIT TRANSACTION 

Tenga en cuenta que el mantenimiento de la BatchSize < 5000 es menos costoso en recursos.

0

Como supongo que la mejor manera de eliminar gran cantidad de registros es eliminarlo por Primary Key. (¿Qué es Primary Keysee here)

Por lo tanto, debe generar la secuencia de comandos tsql que contiene toda la lista de líneas para eliminar y luego ejecutar esta secuencia de comandos.

Por ejemplo de código siguiente se que va a generar el archivo

GO 
SET NOCOUNT ON 

SELECT 'DELETE FROM DATA_ACTION WHERE ID = ' + CAST(ID AS VARCHAR(50)) + ';' + CHAR(13) + CHAR(10) + 'GO' 
FROM DATA_ACTION 
WHERE YEAR(AtTime) = 2014 

El archivo ouput se va a tener registros como

DELETE FROM DATA_ACTION WHERE ID = 123; 
GO 
DELETE FROM DATA_ACTION WHERE ID = 124; 
GO 
DELETE FROM DATA_ACTION WHERE ID = 125; 
GO 

Y ahora usted tiene que utilizar SQLCMD utilidad con el fin de ejecutar este script.

sqlcmd -S [Instance Name] -E -d [Database] -i [Script] 

Puede encontrar este enfoque han explicado aquí https://www.mssqltips.com/sqlservertip/3566/deleting-historical-data-from-a-large-highly-concurrent-sql-server-database-table/

Cuestiones relacionadas