Existe uma maneira de testar se DELETE falhará devido a restrições?

10

Eu gostaria de poder prever se um DELETE terá uma violação de restrição, sem realmente executar a exclusão.

Quais são as minhas opções para fazer isso? Existe uma maneira simples de fazer uma "execução a seco" de um DELETE?

Jay Sullivan
fonte
Você está tentando impedir a exceção apenas para esta declaração ou está tentando facilitar o tratamento de erros em um lote maior que contém essa exclusão?
Aaron Bertrand
3
Você poderia verificar se existe um FK e executar uma instrução SELECT para verificar os valores?
SQLRockstar 23/08/12
Aaron: Precisamos executar um lote de vários DELETEs em transações separadas. Se um falhar, os outros já estão comprometidos. (Design ruim desde o início, eu sei, mas não é meu aplicativo e não está mudando.) A melhor solução no momento parece fazer uma verificação a seco para ver se os DELETEs falharão.
Jay Sullivan
Ainda não tenho certeza se entendi. Você está tentando permitir que o restante das exclusões tenha êxito ou está tentando verificar antecipadamente se todas as exclusões serão bem-sucedidas ou nenhuma delas deve ser?
Aaron Bertrand
Aaron: Desculpe, não deixei claro, mas sim, estou tentando garantir que todos tenham sucesso ou que nenhum deles tenha sucesso.
Jay Sullivan

Respostas:

24

Se seu objetivo é processar todas as exclusões apenas se todas tiverem êxito, por que não usar TRY / CATCH:

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

Se o objetivo é permitir que todas as exclusões bem-sucedidas sejam bem-sucedidas, mesmo que uma ou mais falhe, você pode usar TENTAR / PEGAR individualmente, por exemplo

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

BEGIN TRY
  DELETE #2;
END TRY
BEGIN CATCH
  PRINT 1;
END CATCH
Aaron Bertrand
fonte
6

Uma opção é iniciar uma transação, executar sua exclusão e sempre reverter:

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
GaTechThomas
fonte
11
E se a exclusão for bem-sucedida, execute-a novamente? E se a exclusão for muito cara? E se a exclusão falhar, o que então? Você fez uma exclusão e duas seleções. Como decido avançar ou não na próxima exclusão?
Aaron Bertrand
11
Se isso faz parte dos requisitos, eles devem ser tratados. Esta resposta diz respeito a uma "operação simples" a seco.
GaTechThomas
Bem, eles não estavam quando você enviou sua resposta pela primeira vez, mas foram esclarecidos agora.
Aaron Bertrand
4
@GaTechThomas Aaron contribui muito, então às vezes é breve, mas estou certo de que sua intenção não era ser agressiva. Eu discuti isso no The Heap e ficaria grato pela chance de fazer isso com você também.
Jack diz que tente topanswers.xyz 24/08/12
11
@JackDouglas Eu li os comentários do The Heap que você faz referência e entende o ponto. Os comentários da comunidade foram razoáveis, exceto pela parte em que fui chamado de palhaço por apontar sua agressão. Não entendo como sou o agressivo. Publiquei uma resposta legítima à pergunta como ela foi feita na época. Não exigia qualidade de produção - às vezes você só precisa de maneira rápida e fácil. Então, na minha resposta, recebo perguntas pontuais. A aparência era que ele estava denegrindo minha resposta para que a dele fosse escolhida. Devemos levar esse tópico para outro lugar?
GaTechThomas
0

Gostaria de melhorar a solução fornecida por Aaron Bertrand com algum código, caso você queira adicionar qualquer elemento de uma tabela, gerenciando as exceções para ignorar falhas ou também interromper o processo após erros.

Este selecionará os registros da tabela e tentará excluí-los sem exceções:

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
MAXE
fonte
11
Por quê? Como isso é uma melhoria em relação à resposta aceita?
Home