Question

Il est généralement admis que l'utilisation de curseurs dans les procédures stockées doit être évitée autant que possible (remplacée par une logique basée sur des ensembles, etc.).Si vous prenez les cas où vous devez parcourir certaines données et que vous pouvez le faire en lecture seule, le curseur d'avance rapide (en lecture seule) est-il plus ou moins inefficace que, par exemple, les boucles while ?D'après mes enquêtes, il semble que l'option du curseur soit généralement plus rapide et utilise moins de lectures et de temps processeur.Je n'ai pas fait de tests approfondis, mais est-ce ce que d'autres trouvent ?Les curseurs de ce type (avance rapide) entraînent-ils une surcharge ou des ressources supplémentaires qui pourraient être coûteuses et que je ne connais pas.

Est-ce que tout le discours sur la non-utilisation des curseurs consiste vraiment à éviter l'utilisation de curseurs lorsque des approches basées sur des ensembles sont disponibles, et à utiliser des curseurs actualisables, etc.

Merci

Était-ce utile?

La solution

La « meilleure pratique » consistant à éviter les curseurs dans SQL Server remonte à SQL Server 2000 et aux versions antérieures.La réécriture du moteur en SQL 2005 a résolu la plupart des problèmes liés aux problèmes de curseurs, notamment avec l'introduction de l'option d'avance rapide.Les curseurs ne sont pas nécessairement pires que ceux basés sur des ensembles et sont utilisés largement et avec succès dans Oracle PL/SQL (LOOP).

Le «généralement accepté» auquel vous faites référence était valide, mais est maintenant obsolète et incorrect - partez du principe que les curseurs d'avance rapide se comportent comme annoncé et fonctionnent.Effectuez des tests et des recherches, en basant vos résultats sur SQL2005 et versions ultérieures

Autres conseils

Bien qu'un curseur d'avance rapide présente certaines optimisations dans SQL Server 2005, il est pas il est vrai qu'ils sont proches d'une requête basée sur un ensemble en termes de performances.Il existe très peu de situations dans lesquelles la logique du curseur ne peut pas être remplacée par une requête basée sur un ensemble.Les curseurs seront toujours intrinsèquement plus lents, en partie à cause du fait que vous devez continuer à interrompre l'exécution afin de remplir vos variables locales.

Voici quelques références, qui ne seraient que la pointe de l’iceberg si vous faites des recherches sur cette question :

http://www.code-magazine.com/Article.aspx?quickid=060113

http://dataeducation.com/re-inventing-the-recursive-cte/

Cette réponse espère consolider les réponses données à ce jour.

1) Si possible, utilisez une logique basée sur des ensembles pour vos requêtes, c'est-à-direessayez et utilisez juste SELECT, INSERT, UPDATE ou DELETE avec le approprié FROM clauses ou requêtes imbriquées - celles-ci seront presque toujours plus rapides.

2) Si ce qui précède n'est pas possible, alors dans SQL Server 2005+ FAST FORWARD les curseurs sont efficaces et fonctionnent bien et doivent être utilisés de préférence aux boucles while.

"Si vous souhaitez un curseur encore plus rapide que FAST FORWARD, utilisez un curseur STATIQUE.Ils sont plus rapides que FAST FORWARD.Pas extrêmement rapide mais peut faire la différence. »

Pas si vite!Selon Microsoft :"En général, lorsque ces conversions se produisaient, le type de curseur se dégradait en un type de curseur" plus cher ".Généralement, un curseur (FAST) FORWARD-ONLY est le plus performant, suivi de DYNAMIC, KEYSET et enfin STATIC qui est généralement le moins performant.

depuis: http://blogs.msdn.com/b/mssqlisv/archive/2006/06/23/644493.aspx

Vous pouvez éviter les curseurs la plupart du temps, mais c'est parfois nécessaire.

Gardez simplement à l'esprit que FAST_FORWARD est DYNAMIC...FORWARD_ONLY que vous pouvez utiliser avec un curseur STATIC.

Essayez de l'utiliser sur le problème d'Halloween pour voir ce qui se passe !!!

IF OBJECT_ID('Funcionarios') IS NOT NULL
DROP TABLE Funcionarios
GO

CREATE TABLE Funcionarios(ID          Int IDENTITY(1,1) PRIMARY KEY,
                          ContactName Char(7000),
                          Salario     Numeric(18,2));
GO

INSERT INTO Funcionarios(ContactName, Salario) VALUES('Fabiano', 1900)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Luciano',2050)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Gilberto', 2070)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Ivan', 2090)
GO

CREATE NONCLUSTERED INDEX ix_Salario ON Funcionarios(Salario)
GO

-- Halloween problem, will update all rows until then reach 3000 !!!
UPDATE Funcionarios SET Salario = Salario * 1.1
  FROM Funcionarios WITH(index=ix_Salario)
 WHERE Salario < 3000
GO

-- Simulate here with all different CURSOR declarations
-- DYNAMIC update the rows until all of then reach 3000
-- FAST_FORWARD update the rows until all of then reach 3000
-- STATIC update the rows only one time. 

BEGIN TRAN
DECLARE @ID INT
DECLARE TMP_Cursor CURSOR DYNAMIC 
--DECLARE TMP_Cursor CURSOR FAST_FORWARD
--DECLARE TMP_Cursor CURSOR STATIC READ_ONLY FORWARD_ONLY
    FOR SELECT ID 
          FROM Funcionarios WITH(index=ix_Salario)
         WHERE Salario < 3000

OPEN TMP_Cursor

FETCH NEXT FROM TMP_Cursor INTO @ID

WHILE @@FETCH_STATUS = 0
BEGIN
  SELECT * FROM Funcionarios WITH(index=ix_Salario)

  UPDATE Funcionarios SET Salario = Salario * 1.1 
   WHERE ID = @ID

  FETCH NEXT FROM TMP_Cursor INTO @ID
END

CLOSE TMP_Cursor
DEALLOCATE TMP_Cursor

SELECT * FROM Funcionarios

ROLLBACK TRAN
GO

Les gens évitent les curseurs car ils sont généralement plus difficiles à écrire qu'une simple boucle while. Cependant, une boucle while peut être coûteuse car vous sélectionnez constamment des données dans une table, temporaires ou non.

Avec un curseur en lecture seule, en avance rapide, les données sont conservées en mémoire et ont été spécialement conçues pour la lecture en boucle.

Cet article souligne qu'un curseur moyen s'exécute 50 fois plus vite qu'une boucle while.

Quelques alternatives à l'utilisation du curseur :

Alors que les boucles temporaires dérivées des tables dérivées des sous-requêtes associées Instructions de cas multiples interrogatoires souvent, les opérations de curseur peuvent également être obtenues avec des techniques non-curseurs.

Si vous êtes sûr que le curseur doit être utilisé, le nombre d'enregistrements à traiter doit être réduit autant que possible.Une façon de procéder consiste à traiter d'abord les enregistrements dans une table temporaire, non pas dans la table d'origine, mais dans un curseur qui utilisera les enregistrements de la table temporaire.Lorsque ce chemin est utilisé, on suppose que le nombre d'enregistrements dans la table temporaire a été considérablement réduit par rapport à la table d'origine.Avec moins d'enregistrements, le curseur se termine plus rapidement.

Certaines propriétés du curseur qui affectent les performances incluent :

FORWARD_ONLY :Prend en charge le transfert uniquement du curseur de la première ligne à la fin avec FETCH NEXT.Sauf si elle est définie sur KEYSET ou STATIC, la clause SELECT est réévaluée lorsque chaque extraction est appelée.

STATIQUE:Crée une copie temporaire des données créées et est utilisée par le curseur.Cela évite que le curseur soit recalculé à chaque appel, ce qui améliore les performances.Cela ne permet pas la modification du type de curseur et les modifications apportées à la table ne sont pas reflétées lorsque la récupération est appelée.

JEU DE CLÉS :Les lignes avec curseur sont placées dans une table sous tempdb et les modifications apportées aux colonnes non clés sont reflétées lorsque la récupération est appelée.Cependant, les nouveaux enregistrements ajoutés au tableau ne sont pas reflétés.Avec le curseur keyset, l'instruction SELECT n'est pas réévaluée.

DYNAMIQUE:Toutes les modifications apportées au tableau sont reflétées dans le curseur.Le curseur est réévalué à chaque appel de récupération.Cela utilise beaucoup de ressources et affecte négativement les performances.

AVANCE RAPIDE:Le curseur est unidirectionnel, tel que FORWARD_ONLY, mais spécifie le curseur en lecture seule.FORWARD_ONLY est une augmentation des performances et le curseur n'est pas réévalué à chaque récupération.Il donne les meilleures performances s’il est adapté à la programmation.

OPTIMISTE:Cette option peut être utilisée pour mettre à jour les lignes du curseur.Si une ligne est récupérée et mise à jour et qu'une autre ligne est mise à jour entre les opérations d'extraction et de mise à jour, l'opération de mise à jour du curseur échoue.Si un curseur OPTIMISTIC est utilisé pour effectuer une mise à jour de ligne, il ne doit pas être mis à jour par un autre processus.

NOTE:Si le curseur n'est pas spécifié, la valeur par défaut est FORWARD_ONLY.

Pour répondre aux questions initiales de Mile...

Les curseurs statiques en avance rapide, en lecture seule (affectueusement connus sous le nom de « curseur de tuyau d'incendie ») sont généralement aussi rapides, voire plus rapides, qu'une table temporaire équivalente et une boucle While, car un tel curseur n'est rien de plus qu'une table temporaire et une boucle While qui a été optimisé un peu en coulisses.

Pour ajouter à ce qu'Eric Z.Beard posté sur ce fil et pour répondre davantage à la question de...

"Est-ce que tout le sujet de ne pas utiliser vraiment de curseurs d'éviter l'utilisation de curseurs lorsque des approches basées sur les ensembles sont disponibles, et l'utilisation de curseurs à jour, etc."

Oui.À quelques exceptions près, il faut moins de temps et moins de code pour écrire du code approprié basé sur un ensemble pour faire la même chose que la plupart des curseurs et présente l'avantage supplémentaire d'utiliser beaucoup moins de ressources et s'exécute généralement BEAUCOUP plus rapidement qu'un curseur ou une boucle While.D'une manière générale et à l'exception de certaines tâches administratives, elles devraient vraiment être évitées au profit d'un code basé sur des ensembles correctement écrit.Il existe bien sûr des exceptions à chaque « règle » mais, dans le cas des curseurs, des boucles While et d'autres formes de RBAR, la plupart des gens peuvent compter les exceptions d'une seule main sans utiliser tous les doigts.;-)

Il y a aussi la notion de « RBAR caché ».Il s’agit d’un code qui semble basé sur un ensemble mais qui ne l’est pas en réalité.Ce type de code « basé sur des ensembles » est la raison pour laquelle certaines personnes ont adopté les méthodes RBAR et disent qu'elles sont « OK ».Par exemple, résoudre le problème du total cumulé à l'aide d'une sous-requête corrélée agrégée (SUM) contenant une inégalité pour construire le total cumulé n'est pas vraiment basé sur un ensemble dans mon livre.Au lieu de cela, il s'agit de RBAR sous stéroïdes car, pour chaque ligne calculée, il doit "toucher" à plusieurs reprises de nombreuses autres lignes à un taux de N*(N+1)/2.C'est ce qu'on appelle une « jointure triangulaire » et est au moins deux fois moins mauvaise qu'une jointure cartésienne complète (jointure croisée ou « jointure carrée »).

Bien que MS ait apporté quelques améliorations au fonctionnement des curseurs depuis SQL Server 2005, le terme « Curseur rapide » reste un oxymore par rapport au code basé sur des ensembles correctement écrit.Cela est également vrai même dans Oracle.J'ai travaillé avec Oracle il y a seulement 3 ans, mais mon travail consistait à améliorer les performances du code existant.La plupart des améliorations vraiment substantielles ont été réalisées lorsque j'ai converti les curseurs en code basé sur des ensembles.De nombreuses tâches qui prenaient auparavant de 4 à 8 heures à exécuter ont été réduites à quelques minutes, voire parfois quelques secondes.

Si vous souhaitez un curseur encore plus rapide que FAST FORWARD, utilisez un curseur STATIQUE.Ils sont plus rapides que FAST FORWARD.Pas extrêmement rapide mais peut faire la différence.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top