Mejores Prácticas para hacer un DELETE en SQL Server

Problema

La eliminación de datos en forma directa de una base de datos SQL Server es algo que realmente no me gusta hacer, siempre es mejor si se puede hacer a través de una aplicación que ha sido probada a fondo. Sin embargo, inevitablemente, llega un momento en que es necesario hacer un pequeño borrado de datos. A continuación se presentan algunas de las mejores prácticas que me gusta seguir al borrar datos.

Solución

Antes de entrar en los detalles de estos consejos, que habitualmente incluímos en Nuestros Cursos, sólo quiero mencionar que se debe hacer simpre una copia de  respaldo cuando se va a hacer una eliminación de datos. Sé que todo el mundo sabe cómo son de importantes las copias de seguridad, pero antes de eliminar cualquier cosa, probablemente quieras verificar que tienes una buena copia de respaldo. Nunca se puede ser demasiado cuidadoso cuando se trata de la eliminación de datos.

Dicho esto, vamos a configurar un par tablas para realizar nuestras consultas de ejemplo. La primer tabla es la que contiene los datos reales y la segunda contiene una lista de registros que se eliminan de la tabla principal.

Aquí está el T-SQL para crear y cargar estas tablas.

 

Inicia 23 de Mayo

 

 

 

--Table Setup
CREATE TABLE Main (col1 INT, col2 INT);
CREATE TABLE ToDelete (col3 INT);

DECLARE @val INT
SELECT @val=1
WHILE @val < 50000
BEGIN  
   INSERT INTO Main VALUES (@val,round(rand()*100000,0));
   IF (@val % 1000) = 0
      INSERT INTO ToDelete VALUES (@val);
   SELECT @val=@val+1;
END;

 

Ahora vamos a eliminar los registros de la tabla principal en base a los registros de la tabla ToDelete utilizando la siguiente consulta sencilla.

 

DELETE FROM Main WHERE col1 IN (SELECT col1 From ToDelete);

(49999 row(s) affected)

 

Vaya, parece que se han eliminado demasiadas filas, ¿Que pasó? Si nos fijamos bien, en mi consulta puse mal por accidente el nombre de la columna en la sub consulta, y porque existe este nombre de columna en la tabla principal he quitado todo. Veamos algunas maneras diferentes que podríamos haber evitado este problema.

Para nuestra primera solución puedo utilizar alias de tablas en cada tabla, esto entonces no habría sucedido y la consulta daría el error de la siguiente manera:

 

DELETE FROM Main WHERE col1 IN (SELECT td.col1 FROM ToDelete td);

Msg 207, Level 16, State 1, Line 2
Invalid column name 'col1'.

 

En este caso, entonces, podría fijar la consulta para utilizar el nombre de la columna correcta y podría eliminar los registros que se suponía iban a ser eliminados en primer lugar.

 

DELETE FROM Main WHERE col1 IN (SELECT td.col3 FROM ToDelete td)
 
(49 row(s) affected)

 

Inicia 23 de Mayo

 

 

Otra solución a este problema, y que utilizo muy a menudo, es comprobar siempre cómo van a ser eliminados antes de ejecutar el borrado de muchos registros. Si creamos y ejecutamos el script siguiente cuando necesitamos borrar registros, nos daríamos cuenta de que se iban a eliminar demasiados registros, dado que el SELECT devuelve 49999 registros. Siempre comento la parte del DELETE de la secuencia de comandos para que no sea ejecutado por accidente.

 

SELECT COUNT(1)
--DELETE 
FROM Main WHERE col1 IN (SELECT col1 From ToDelete);

(No column name)
49999

 

 

Una vez que veo que el script devuelve demasiados registros, puedo arreglarlo y luego ejecutarlos con éxito como se muestra a continuación.

 

SELECT COUNT(1)
--DELETE 
FROM Main WHERE col1 IN (SELECT col3 From ToDelete);

-- SELECT output
(No column name)
49

-- DELETE output
(49 row(s) affected)

 

 

Una buena práctica final para usar cuando se eliminan los datos de una base de datos es envolver siempre la instrucción DELETE en un bloque de código BEGIN TRAN – COMMIT/ROLLBACK TRAN. Con el método descrito a continuación se puede ejecutar el BEGIN TRAN y hacer el DELETE, y a continuación, comprobar cuántos registros se ven afectados antes de confirmar los cambios. Tal como se hizo en la solución anterior, he comentado la parte del COMMIT para que no se ejecute por accidente.

 

BEGIN TRAN
DELETE FROM Main WHERE col1 IN (SELECT col1 From ToDelete);
--ROLLBACK TRAN
--COMMIT TRAN

-- DELETE output
(49999 row(s) affected)

Al igual que en el ejemplo anterior, una vez que veo que se eliminaron demasiadas filas, puedo deshacer la transacción con un ROLLBACK y arreglar el script como se muestra a continuación. En este caso se devuelve el número correcto de registros y luego puedo ejecutar el COMMIT.

 

BEGIN TRAN
DELETE FROM Main WHERE col1 IN (SELECT col3 From ToDelete);
--ROLLBACK TRAN
--COMMIT TRAN

-- DELETE output
(49 row(s) affected)

Siempre entonces es bueno asegurarse de que se tiene una copia de seguridad de sus datos y si es posible probar los scripts de eliminación en una copia de la base de datos de producción antes de ejecutar las actualizaciones o eliminaciones. Incluso lo que se cree que puede ser una pequeño UPDATE o DELETE, podría llegar a ser un gran problema con la sintaxis incorrecta.

Inicia 23 de Mayo