2012-01-25 9 views
11

estoy corriendo el siguiente comando para eliminar filas en lotes de una mesa grande (150 millones de filas):La supresión de filas en la tabla causa ASEGURA

DECLARE @RowCount int 
WHILE 1=1 
    BEGIN 
     DELETE TOP (10000) t1 
     FROM table t1 
     INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey 
     WHERE t1.YearProcessed <= 2007 

     SET @RowCount = @@ROWCOUNT 

     IF (@RowCount < 10000) BREAK 
    END 

Esta tabla se utiliza ALTAMENTE. Sin embargo, está borrando registros, pero también está causando bloqueo en algunos registros, arrojando así errores al usuario (lo que no es aceptable en el entorno en el que nos encontramos).

¿Cómo puedo eliminar registros antiguos sin causar bloqueos? ¿Debo reducir el tamaño del lote de 10000 registros a 1000? Cómo afectará esto a los tamaños de registro (tenemos muy poco espacio en el disco duro para un crecimiento de registro grande).

¿Alguna sugerencia?

Respuesta

7

he visto problemas esporádicos similares en el pasado, donde incluso en pequeños lotes 0f 5000 registros, el bloqueo todavía sucedería En nuestro caso, cada eliminación/actualización estaba contenida en su propio ciclo Begin Begin ... Commit.Para corregir el problema, la lógica de

WaitFor RETRASO '00: 00: 00: 01'

se colocó en la parte superior de cada bucle a través de y que corrige el problema.

+0

¿Está esto al principio/final de cada lote o eliminación de cada fila? – Sean

1

Creo que estás en el camino correcto.

mirada a estos dos artículos, también:

y:

http://www.dbforums.com/microsoft-sql-server/985516-deleting-without-locking.html

Antes de ejecutar la eliminación, verifique el plan de consulta estimado para ver si está realizando una búsqueda de índice para la eliminación, o sigue haciendo una tabla completa escaneo/acceso.

4

En primer lugar - parece que su DELETE realizar índice agrupado de escaneo, recomiendo hacer lo siguiente:

create index [IX.IndexName] ON t1(YearProcessed, PrimaryKey) 

En segundo lugar - ¿hay alguna necesidad para unirse a la tabla t2?

y luego usar siguiente consulta a eliminar las filas, en el supuesto de que su columna PrimaryKey es de tipo INT:

declare @ids TABLE(PrimaryKey INT) 
WHILE 1=1 
    BEGIN 
     INSERT @ids 
     SELECT top 10000 DISTINCT t1.PrimaryKey 
     FROM table t1 
     INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey 
     WHERE t1.YearProcessed <= 2007 

     IF @@ROWCOUNT = 0 BREAK 

     DELETE t1 
     WHERE PrimaryKey in (Select PrimaryKey from @ids) 

     delete from @ids 

    END 

Y no se olvide de quitar la tabla t2 de unirse a si no se necesita

Si todavía causa bloqueos, disminuya la cantidad de filas eliminadas en cada ronda

+0

realidad estoy interior que llegó procedente de una tabla padre una lista de las claves principales para borrar (que es en realidad en una variable de tabla). Acabo de publicar dos tablas para simplificar las secuencias de comandos. Muy similar a lo que hiciste, pero tomaste un enfoque diferente que voy a probar. – Sean

0

Además de las otras sugerencias (que tienen como objetivo reducir el trabajo realizado durante la eliminación) también puede configurar SQL Server para no bloquear a otros lectores mientras realiza elimina en at poder.

Esto se puede hacer mediante el uso de "aislamiento de instantánea" que se introdujo con SQL Server 2005:

http://msdn.microsoft.com/en-us/library/ms345124%28v=sql.90%29.aspx

+1

Es bueno tener en cuenta que el aislamiento de instantáneas requiere cambiar el nivel de aislamiento para una sola transacción. El aislamiento de instantánea confirmado de lectura cambiará el nivel de aislamiento predeterminado, pero requiere una interrupción muy breve. –

0

Si tiene algo con eliminaciones en cascada, asegúrese de que estén indexadas.

Resaltando la consulta DELETE y haciendo clic en Display estimated execution plan se mostrarán los índices sugeridos, que en mi caso incluyeron algunas eliminaciones en cascada.

Añadiendo índices para aquellos que hicieron la eliminación mucho más rápido, pero aún así no intentaría eliminar todas las filas a la vez.

0

la mejor manera que he encontrado es de asp.net DeleteExpiredSessions. hace una selección READUNCOMMITTED y coloca los registros en una tabla temporal, que elimina el registro con un CURSOR.

ALTER PROCEDURE [dbo].[DeleteExpiredSessions] 
    AS 
     SET NOCOUNT ON 
     SET DEADLOCK_PRIORITY LOW 

     DECLARE @now datetime 
     SET @now = GETUTCDATE() 

     CREATE TABLE #tblExpiredSessions 
     ( 
      SessionID nvarchar(88) NOT NULL PRIMARY KEY 
     ) 

     INSERT #tblExpiredSessions (SessionID) 
      SELECT SessionID 
      FROM [ASPState].dbo.ASPStateTempSessions WITH (READUNCOMMITTED) 
      WHERE Expires < @now 

     IF @@ROWCOUNT <> 0 
     BEGIN 
      DECLARE ExpiredSessionCursor CURSOR LOCAL FORWARD_ONLY READ_ONLY 
      FOR SELECT SessionID FROM #tblExpiredSessions 

      DECLARE @SessionID nvarchar(88) 

      OPEN ExpiredSessionCursor 

      FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID 

      WHILE @@FETCH_STATUS = 0 
       BEGIN 
        DELETE FROM [ASPState].dbo.ASPStateTempSessions WHERE SessionID = @SessionID AND Expires < @now 
        FETCH NEXT FROM ExpiredSessionCursor INTO @SessionID 
       END 

      CLOSE ExpiredSessionCursor 

      DEALLOCATE ExpiredSessionCursor 

     END 

     DROP TABLE #tblExpiredSessions 

    RETURN 0 
0

Prueba de esto,

DECLARE @RowCount int 
WHILE 1=1 
    BEGIN 
     BEGIN TRANSACTION 
     DELETE TOP (10000) t1 
     FROM table t1 
     INNER JOIN table2 t2 ON t2.PrimaryKey = t1.PrimaryKey 
     WHERE t1.YearProcessed <= 2007 
     END TRANSACTION 
     COMMIT TRANSACTION 
     SET @RowCount = @@ROWCOUNT 

     IF (@RowCount < 10000) BREAK 
    END 
Cuestiones relacionadas