¿Hay alguna forma de probar si DELETE fallará debido a restricciones?


10

Me gustaría poder predecir si un DELETE se encontrará con una violación de restricción, sin realizar realmente la eliminación.

¿Cuáles son mis opciones para hacer esto? ¿Hay una manera simple de hacer una "ejecución en seco" de un DELETE?


¿Está tratando de evitar la excepción solo para esta declaración, o está tratando de facilitar el manejo de errores en un lote más grande que contiene esta eliminación?
Aaron Bertrand

3
¿Podría verificar si existe un FK y ejecutar una instrucción SELECT para verificar los valores?
SQLRockstar

Aaron: Necesitamos ejecutar un lote de varios DELETEs en transacciones separadas. Si uno falla, los otros ya están comprometidos. (Mal diseño desde el principio, lo sé, pero no es mi aplicación y no está cambiando). La mejor solución en este momento suena como hacer una verificación en seco para ver si los DELETEs fallarán.
Jay Sullivan el

Todavía no estoy seguro de entender. ¿Está tratando de permitir que el resto de las eliminaciones tengan éxito, o está tratando de verificar por adelantado que todas las eliminaciones tendrán éxito, o ninguna de ellas debería hacerlo?
Aaron Bertrand

Aaron: Lo siento, no lo dejé claro, pero sí, estoy tratando de asegurarme de que todos tengan éxito, o ninguno de ellos tenga éxito.
Jay Sullivan el

Respuestas:


24

Si su objetivo es procesar todas las eliminaciones solo si todas tienen éxito, ¿por qué no usar TRY / CATCH?

BEGIN TRANSACTION;
BEGIN TRY
  DELETE #1;
  DELETE #2;
  DELETE #3;
  COMMIT TRANSACTION;
END TRY
BEGIN CATCH
  ROLLBACK TRANSACTION;
END CATCH

Si el objetivo es permitir que todas las eliminaciones exitosas tengan éxito incluso si una o más fallan, entonces puede usar TRY / CATCH individual, por ejemplo

BEGIN TRY
  DELETE #1;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH

BEGIN TRY
  DELETE #2;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH

6

Una opción es comenzar una transacción, ejecutar su eliminación y luego siempre revertir:

begin tran

delete Table1 where col1 = 1

-- Test whether it is there
select * from Table1 where col1 = 1

rollback tran

-- Confirm that it is still there
select * from Table1 where col1 = 1

1
Y si la eliminación se realiza correctamente, ¿volver a ejecutarla? ¿Qué pasa si la eliminación es muy costosa? Y si la eliminación falla, ¿entonces qué? Has hecho una eliminación y dos selecciones. ¿Cómo decido pasar a la próxima eliminación o no?
Aaron Bertrand

1
Si esos son parte de los requisitos, entonces deben ser manejados. Esta respuesta se refiere a una "simple 'prueba en seco'".
GaTechThomas

Bueno, no lo fueron cuando envió su respuesta por primera vez, pero ahora se han aclarado.
Aaron Bertrand

44
@GaTechThomas Aaron contribuye mucho, por lo que a veces es breve, pero estoy seguro de que su intención no era ser agresivo. Discutí esto en The Heap y agradecería la oportunidad de hacerlo también contigo.
Jack dice que intente topanswers.xyz

1
@JackDouglas He leído los comentarios de The Heap a los que hace referencia y entiende el punto. Los comentarios de la comunidad fueron razonables, excepto por la parte en la que me llamaron payaso por señalar su agresión. No entiendo cómo soy el que fue visto como agresivo. Publiqué una respuesta legítima a la pregunta tal como se planteó en ese momento. No pedía calidad de producción, a veces solo se necesita de manera rápida y fácil. Entonces, en mi respuesta, recibo preguntas puntuales. La apariencia era que él estaba denigrando mi respuesta para que la suya fuera elegida. ¿Deberíamos llevar este hilo a otra parte?
GaTechThomas

0

Me gustaría mejorar la solución provista por Aaron Bertrand con algún código, en caso de que desee intentar agregar algún elemento de una tabla, administrar las excepciones para ignorar fallas o también detener el proceso después de los errores.

Éste seleccionará los registros de la tabla y luego intenta eliminarlos sin excepciones:

DECLARE @MaxErrors INT
SET @MaxErrors = 5;    // Setting 0 will stop process after the first error!

SELECT
    [Id]
    , ROW_NUMBER() OVER (ORDER BY Id ASC) AS [Index]
INTO #DeletingItems
FROM myTable

DECLARE @Current INT, @Max INT, @Id INT, @TotErrors INT
SELECT
    @Current = 1
    , @TotErrors = 0
    , @Max = MAX([Index])
FROM #DeletingTable

WHILE @Current <= @Max
BEGIN
    SELECT
        @Id = [Id]
    FROM #DeletingItems
    WHERE
        [Index] = @Index;

    BEGIN TRANSACTION;    
    BEGIN TRY    
        DELETE FROM myTable WHERE [Id] = @Id;

        COMMIT TRANSACTION;    
    END TRY    
    BEGIN CATCH    
        ROLLBACK TRANSACTION;

        SET @TotErrors = @TotErrors + 1;

        IF @TotErrors > @MaxErrors
            BREAK;
    END CATCH

    SET @Current = @Current + 1;
END

1
¿Por qué? ¿Cómo es esto una mejora sobre la respuesta aceptada?
ToolmakerSteve
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.