SQL Server – Supprimer des variables de table pas instantané?

J'ai hérité de la gestion d'une procédure stockée qui est exécutée tous les soirs via un travail d'agent SQL. Cela fonctionne bien depuis des mois, mais tout à coup, les dernières nuits ont fait double emploi et ont manqué un peu de travail.

Le travail se déroule au milieu de la nuit, et il n'y a pas d'users pour le moment. J'ai restauré une sauvegarde de la database juste avant l'exécution problématique sur un server de test, j'ai ré-exécuté la procédure et tout a bien fonctionné. C'est aussi de petites données, peut-être 100-200 lignes par nuit.

Voici une représentation de l'une des loops de la procédure où le problème a été rencontré:

DECLARE @uniqueId int DECLARE @examId int DECLARE @TempSingleContactTable TABLE ( uniqueId int IDENTITY(1,1) PRIMARY KEY, examId int not null, contactEmail nvarchar(max) null, ) [data inserted into @TempSingleContactTable] WHILE EXISTS (SELECT * FROM @TempSingleContactTable) BEGIN Select top 1 @uniqueId = uniqueId, @examId = examID, from @TempSingleContactTable [*****PROBLEM HERE- this line with same value for @examId ran multiple times, but eventually continued] DELETE FROM @TempSingleContactTable WHERE examID = @examId END 

La seule chose que je peux voir qui pourrait causer le problème ci-dessus est si l'appel DELETE n'a pas fonctionné. Est-il possible qu'un appel DELETE contre une variable de table n'est pas instantané?


EDIT: Toute information sur ce qui pourrait causer l'échec de la suppression de @TempSingleContactTable de façon sporadique est très appréciée.


EDIT 2: Une enquête supplémentaire a montré que cette procédure automatisée une fois par nuit a échoué de la même manière deux fois en deux mois. Fait intéressant, chaque fois qu'il a échoué, la course de la nuit précédente n'a pas modifié datatables et il devrait toujours. Malheureusement, il n'y a pas d'informations de journalisation pour déterminer ce qui a pu causer les problèmes des nuits précédentes. Il semble qu'ils doivent être liés, même si cela pourrait être un faux-fuyant. J'ai ajouté la notation avec l'espoir d'arriver à la cause sous-jacente réelle.

De quoi il semble que vous ayez hérité d'un "slider de pauvre". D'une manière ou d'une autre, les gens ont entendu dire que les sliders sont 'mauvais' et ils ont alors trouvé ceci = (Je ne vais pas commencer un débat sur la façon dont les sets sont préférés aux opérations basées sur le slider. vous n'avez simplement pas le choix, peut-être que c'est aussi le cas.

Convertir votre boucle en un slider décent devrait probablement «stabiliser» cette partie de la boucle; mais il montre aussi immédiatement qu'il y a un peu de 'problème' avec votre boucle.

A première vue, le slider équivalent serait ceci:

 DECLARE @uniqueId int DECLARE @examId int DECLARE @TempSingleContactTable TABLE ( uniqueId int IDENTITY(1,1) PRIMARY KEY, examId int not null, contactEmail nvarchar(max) null ) -- [data inserted into @TempSingleContactTable] DECLARE exams_loop CURSOR LOCAL FAST_FORWARD FOR SELECT uniqueId, examID FROM @TempSingleContactTable OPEN exams_loop FETCH NEXT FROM exams_loop INTO @uniqueId, @examId WHILE @@FETCH_STATUS = 0 BEGIN -- internals... FETCH NEXT FROM exams_loop INTO @uniqueId, @examId END CLOSE exams_loop DEALLOCATE exams_loop 

Mais quand on regarde de plus près, il y a un crochet: la fin de votre boucle supprime tous les loggings pour un examID donné. Donc, s'il y a plusieurs loggings avec le même examID , cela signifie que certaines valeurs uniqueID seront ignorées. (remarque: il n'est même pas certain lesquels, ne jamais être tenté de countr sur eux étant dans l'ordre naturel car il y a un PK sur le terrain!)

En tant que tel, le code suivant est un meilleur rlocation:

 DECLARE exams_loop CURSOR LOCAL FAST_FORWARD FOR SELECT MIN(uniqueId), examID FROM @TempSingleContactTable GROUP BY examID OPEN exams_loop FETCH NEXT FROM exams_loop INTO @uniqueId, @examId WHILE @@FETCH_STATUS = 0 BEGIN -- internals... FETCH NEXT FROM exams_loop INTO @uniqueId, @examId END CLOSE exams_loop DEALLOCATE exams_loop 

Cette fois, il s'agira en fait de l' uniqueID le plus uniqueID qui l' uniqueID au lieu d'un random, mais en toute honnêteté, je pense que la répétabilité (dont nous parlons ici) doit être préférée à l'random.

Quoi qu'il en soit, en résumé:

  • plutôt utiliser un vrai slider au lieu du rlocation d'un pauvre, car c'est un mauvais rlocation pour commencer avec
  • Si vous voulez vraiment conserver la boucle telle qu'elle est en ce moment, changez la définition de la table en:

=>

 DECLARE @TempSingleContactTable TABLE ( uniqueId int IDENTITY(1,1) PRIMARY KEY, examId int not null UNIQUE (examId, uniqueId), contactEmail nvarchar(max) null ) 

De cette façon, vous aurez au less un index sur le champ lorsque vous supprimez. (même si je déconseille fortement les opérations intensives sur les variables @ table, elles ont tendance à aller vers le sud une fois que vous mettez des quantités de données "moyennes", et encore less commencer à faire des opérations … # temp-tables robuste à cet égard!)