Existe-t-il un moyen de tester si DELETE échouera en raison de contraintes?

10

J'aimerais pouvoir prédire si un DELETE se heurtera à une violation de contrainte, sans réellement effectuer la suppression.

Quelles sont mes options pour ce faire? Existe-t-il un moyen simple de faire un "essai à sec" d'une SUPPRESSION?

Jay Sullivan
la source
Essayez-vous d'empêcher l'exception pour cette seule instruction, ou essayez-vous de faciliter la gestion des erreurs dans un lot plus important contenant cette suppression?
Aaron Bertrand
3
Pourriez-vous vérifier si un FK existe et exécuter une instruction SELECT pour vérifier les valeurs?
SQLRockstar
Aaron: Nous devons exécuter un lot de plusieurs DELETE dans des transactions distinctes. Si l'un échoue, les autres sont déjà engagés. (Mauvais design depuis le début, je sais, mais ce n'est pas mon application, et ça ne change pas.) La meilleure solution de contournement pour le moment ressemble à une vérification à sec pour voir si les DELETE échoueront.
Jay Sullivan
Je ne suis toujours pas sûr de comprendre. Essayez-vous de laisser le reste des suppressions réussir, ou essayez-vous de vérifier à l'avance que toutes les suppressions réussiront, ou qu'aucune d'entre elles ne devrait?
Aaron Bertrand
Aaron: Désolé, je n'ai pas été clair, mais oui, j'essaie de m'assurer qu'ils réussissent tous, ou qu'aucun d'entre eux ne réussit.
Jay Sullivan

Réponses:

24

Si votre objectif est de traiter toutes les suppressions uniquement si elles réussissent toutes, pourquoi ne pas simplement utiliser TRY / CATCH:

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

Si l'objectif est de permettre à toutes les suppressions réussies de réussir même si une ou plusieurs échouent, alors vous pouvez utiliser un TRY / CATCH individuel, par exemple

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
la source
6

Une option consiste à commencer une transaction, exécuter votre suppression, puis toujours annuler:

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
la source
1
Et si la suppression réussit, exécutez-la à nouveau? Et si la suppression coûte très cher? Et si la suppression échoue, alors quoi? Vous avez effectué une suppression et deux sélections. Comment puis-je décider de passer à la prochaine suppression ou non?
Aaron Bertrand
1
Si ceux-ci font partie des exigences, ils doivent être traités. Cette réponse concerne un "simple" essai à sec "".
GaTechThomas
Eh bien, ce n'était pas le cas lorsque vous avez soumis votre réponse pour la première fois, mais elles ont maintenant été clarifiées.
Aaron Bertrand
4
@GaTechThomas Aaron contribue beaucoup, donc il est parfois bref, mais je suis certain que son intention n'était pas d'être agressif. J'en ai discuté dans The Heap et je vous serais reconnaissant de pouvoir le faire également avec vous.
Jack dit d'essayer topanswers.xyz
1
@JackDouglas J'ai lu les commentaires du tas que vous référencez et comprenez le point. Les commentaires de la communauté étaient raisonnables, sauf pour la partie où j'ai été appelé un clown pour avoir souligné son agression. Je ne comprends pas comment je suis celui qui était considéré comme agressif. J'ai posté une réponse légitime à la question telle qu'elle était posée à l'époque. Il ne demandait pas la qualité de la production - parfois, vous avez juste besoin d'une solution rapide et facile. Donc, sur ma réponse, je reçois des questions pointues. Apparemment, il dénigrait ma réponse pour que la sienne soit choisie. Faut-il prendre ce fil ailleurs?
GaTechThomas
0

Je voudrais améliorer la solution fournie par Aaron Bertrand avec du code, au cas où vous voudriez ajouter un élément d'une table, gérer les exceptions pour ignorer les échecs ou arrêter le processus après les erreurs.

Celui-ci sélectionnera les enregistrements de la table puis essaiera de les supprimer sans exception:

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
la source
1
Pourquoi? En quoi est-ce une amélioration par rapport à la réponse acceptée?
ToolmakerSteve