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é:
=>
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!)