web-dev-qa-db-fra.com

Pourquoi les gens détestent-ils autant les curseurs SQL?

Je peux comprendre que je veuille éviter d’avoir à utiliser un curseur en raison des frais généraux et des inconvénients, mais il semble y avoir une grave phobie-maniaque du curseur où les gens s’efforcent de ne pas en utiliser un.

Par exemple, une question demandait comment faire quelque chose de manifestement trivial avec un curseur et la réponse acceptée proposée à l'aide d'une requête récursive d'expression de table commune (CTE) avec une fonction personnalisée récursive, même si cela limite le nombre de lignes pouvant être traitées à 32 (en raison de la limite d'appels de fonction récursive sur le serveur SQL). Cela me semble une solution épouvantable pour la longévité du système, sans parler d'un effort considérable pour éviter d'utiliser un simple curseur.

Quelle est la raison de ce niveau de haine folle? Certaines "autorités notées" ont-elles publié une fatwa contre les curseurs? Un mal innommable se cache-t-il au cœur des curseurs qui corrompt la morale des enfants ou quelque chose du genre?

Question Wiki, plus intéressée par la réponse que le représentant.

Informations connexes:

Curseurs d'avance rapide SQL Server

EDIT: permettez-moi d'être plus précis: je comprends que les curseurs ne doivent pas être utilisés à la place d'opérations relationnelles normales ; c'est une évidence. Ce que je ne comprends pas, ce sont les gens qui font tout ce qu'ils peuvent pour éviter les curseurs comme s'ils avaient des cooties ou quelque chose du genre, même lorsqu'un curseur est une solution plus simple et/ou plus efficace. C'est la haine irrationnelle qui me déconcerte, pas l'efficacité technique évidente.

126
Steven A. Lowe

La "surcharge" avec les curseurs fait simplement partie de l'API. Les curseurs sont la façon dont certaines parties du SGBDR fonctionnent sous le capot. Souvent CREATE TABLE et INSERT ont des instructions SELECT, et l'implémentation est l'implémentation évidente du curseur interne.

L'utilisation "d'opérateurs basés sur des ensembles" de niveau supérieur regroupe les résultats du curseur dans un seul ensemble de résultats, ce qui signifie moins d'API en va-et-vient.

Les curseurs sont antérieurs aux langues modernes qui fournissent des collections de première classe. L'ancien C, COBOL, Fortran, etc., devait traiter les rangées une par une car il n'existait aucune notion de "collection" pouvant être utilisée à grande échelle. Java, C #, Python, etc., ont des structures de liste de première classe pour contenir des ensembles de résultats.

Le problème lent

Dans certains cercles, les jointures relationnelles sont un mystère et les gens écriront des curseurs imbriqués plutôt qu'une simple jointure. J'ai vu des opérations de boucle imbriquées vraiment épiques écrites sous forme de curseurs. Vaincre une optimisation de SGBDR. Et courir très lentement.

De simples réécritures SQL pour remplacer les boucles de curseur imbriquées par des jointures et une seule boucle de curseur plate peuvent faire en sorte que les programmes soient exécutés 100 fois plus vite. [Ils pensaient que j'étais le dieu de l'optimisation. Je n'ai fait que remplacer les boucles imbriquées par des jointures. Toujours utilisé les curseurs.]

Cette confusion mène souvent à une inculpation de curseurs. Cependant, ce n'est pas le curseur, c'est le mauvais usage du curseur qui pose problème.

Le problème de la taille

Pour les ensembles de résultats vraiment épiques (c’est-à-dire le transfert d’une table dans un fichier), les curseurs sont essentiels. Les opérations basées sur les ensembles ne peuvent pas matérialiser des ensembles de résultats très volumineux sous la forme d'une collection unique en mémoire.

Alternatives

J'essaie d'utiliser une couche ORM autant que possible. Mais cela a deux objectifs. Premièrement, les curseurs sont gérés par le composant ORM. Deuxièmement, le code SQL est séparé de l'application dans un fichier de configuration. Ce n'est pas que les curseurs sont mauvais. C’est que coder toutes ces ouvertures, fermetures et extractions n’est pas une programmation à valeur ajoutée.

73
S.Lott

Les curseurs incitent les gens à appliquer excessivement un état d'esprit procédural à un environnement basé sur des ensembles.

Et ils sont [~ # ~] lents [~ # ~] !!!

De SQLTeam :

Veuillez noter que les curseurs sont le moyen le plus lent d’accéder aux données dans SQL Server. Ceci ne devrait être utilisé que lorsque vous avez vraiment besoin d'accéder à une ligne à la fois. La seule raison pour laquelle je peux penser à cela est d'appeler une procédure stockée sur chaque ligne. Dans le article sur les performances du curseur , j'ai découvert que les curseurs sont plus de trente fois plus lents que les alternatives basées sur des ensembles .

41
Galwegian

La réponse ci-dessus indique que "les curseurs sont le moyen le plus lent d'accès aux données dans SQL Server ... les curseurs sont plus de trente fois plus lents que les alternatives basées sur des ensembles".

Cette déclaration peut être vraie dans de nombreuses circonstances, mais en tant que déclaration générale, elle pose problème. Par exemple, j'ai bien utilisé les curseurs dans les situations où je souhaite effectuer une opération de mise à jour ou de suppression affectant de nombreuses lignes d'une grande table qui reçoit des lectures de production constantes. L'exécution d'une procédure stockée qui effectue ces mises à jour ligne par ligne finit par être plus rapide que les opérations basées sur les ensembles, car l'opération basée sur les ensembles est en conflit avec l'opération de lecture et finit par provoquer des problèmes de verrouillage terribles (et peut tuer complètement le système de production, dans des cas extrêmes).

En l'absence d'autres activités dans la base de données, les opérations basées sur les ensembles sont universellement plus rapides. Dans les systèmes de production, cela dépend.

19
davidcl

Les curseurs ont tendance à être utilisés par les développeurs SQL débutants aux endroits où les opérations basées sur les ensembles seraient meilleures. En particulier, lorsque les gens apprennent le langage SQL après avoir appris un langage de programmation traditionnel, la mentalité "itérer sur ces enregistrements" a tendance à amener les gens à utiliser les curseurs de manière inappropriée.

La plupart des livres SQL sérieux incluent un chapitre sur l'utilisation de curseurs; ceux qui sont bien écrits indiquent clairement que les curseurs ont leur place mais ne doivent pas être utilisés pour des opérations basées sur des ensembles.

Il existe évidemment des situations où les curseurs sont le bon choix, ou au moins un bon choix.

9
davidcl

Souvent, l'optimiseur ne peut pas utiliser l'algèbre relationnelle pour transformer le problème lorsqu'une méthode de curseur est utilisée. Souvent, un curseur est un excellent moyen de résoudre un problème, mais SQL est un langage déclaratif et la base de données contient de nombreuses informations, des contraintes aux statistiques, en passant par les index, ce qui signifie que l'optimiseur dispose de nombreuses options pour résoudre le problème. problème, alors qu'un curseur dirige assez explicitement la solution.

9
Cade Roux

Dans Oracle, les curseurs PL/SQL n'entraînent pas de verrous de table et il est possible d'utiliser la collecte en bloc/l'extraction en bloc.

Dans Oracle 10, le curseur implicite souvent utilisé

  for x in (select ....) loop
    --do something 
  end loop;

récupère implicitement 100 lignes à la fois. La collecte en bloc explicite/la récupération en bloc est également possible.

Toutefois, les curseurs PL/SQL ne sont qu’une solution de dernier recours. Utilisez-les lorsque vous ne pouvez pas résoudre un problème avec du SQL basé sur les ensembles.

Une autre raison est la parallélisation, il est plus facile pour la base de données de paralléliser de grandes instructions basées sur des ensembles que du code impératif ligne par ligne. C'est la même raison pour laquelle la programmation fonctionnelle devient de plus en plus populaire (Haskell, F #, LISP, LINQ C #, MapReduce ...), la programmation fonctionnelle facilite la parallélisation. Le nombre de processeurs par ordinateur étant en augmentation, la parallélisation devient de plus en plus problématique.

8
tuinstoel

Les réponses ci-dessus n'ont pas assez souligné l'importance du verrouillage. Je ne suis pas un grand fan des curseurs car ils entraînent souvent des verrous au niveau de la table.

6
Richard T

En général, dans une base de données relationnelle, les performances du code utilisant des curseurs sont d'un ordre de grandeur pire que les opérations basées sur des ensembles.

6
Charles Bretana

Pour ce que ça vaut, j’ai lu que le "un" endroit où un curseur va exécuter son équivalent basé sur un ensemble est dans un total cumulé. Sur une petite table, la vitesse de sommation des lignes sur l'ordre par colonnes favorise l'opération basée sur les ensembles, mais à mesure que la taille de la table augmente, le curseur devient plus rapide car il peut simplement porter la valeur totale courante au prochain passage de la ligne. boucle. Maintenant vous devriez faire un total cumulé est un argument différent ...

3
Eric Sabine

En dehors des problèmes de performances (non), je pense que le plus gros défaut des curseurs est qu’ils sont difficiles à déboguer. Surtout par rapport au code dans la plupart des applications clientes où le débogage a tendance à être relativement facile et les fonctionnalités de langage ont tendance à être beaucoup plus faciles. En fait, je soutiens que presque tout ce que l'on fait en SQL avec un curseur devrait probablement se produire dans l'application cliente en premier lieu.

2
Wyatt Barnett
2
Edin Omeragic

Vous auriez probablement pu conclure votre question après le deuxième paragraphe, plutôt que de qualifier de "fou" les gens simplement parce qu'ils ont un point de vue différent de vous et d'essayer de se moquer des professionnels qui ont peut-être une très bonne raison de se sentir comme eux.

En ce qui concerne votre question, bien qu’il existe certainement des situations dans lesquelles un curseur peut être appelé, les développeurs décident, selon mon expérience, qu’un curseur "doit" être utilisé FAR plus souvent qu’il n’est en réalité. Le risque que quelqu'un se trompe en raison d'une utilisation excessive des curseurs ou de ne pas les utiliser quand ils le devraient est BEAUCOUP plus élevé à mon avis.

1
Tom H

Pouvez-vous poster cet exemple de curseur ou un lien vers la question? Il y a probablement une solution encore meilleure qu'un CTE récursif.

Outre les autres commentaires, les curseurs utilisés incorrectement (ce qui est souvent le cas) provoquent des verrous de page/ligne inutiles.

1
Gordon Bell

en gros 2 blocs de code qui font la même chose. C'est peut-être un exemple un peu étrange mais cela prouve le point. SQL Server 2005:

SELECT * INTO #temp FROM master..spt_values
DECLARE @startTime DATETIME

BEGIN TRAN 

SELECT @startTime = GETDATE()
UPDATE #temp
SET number = 0
select DATEDIFF(ms, @startTime, GETDATE())

ROLLBACK 

BEGIN TRAN 
DECLARE @name VARCHAR

DECLARE tempCursor CURSOR
    FOR SELECT name FROM #temp

OPEN tempCursor

FETCH NEXT FROM tempCursor 
INTO @name

SELECT @startTime = GETDATE()
WHILE @@FETCH_STATUS = 0
BEGIN

    UPDATE #temp SET number = 0 WHERE NAME = @name
    FETCH NEXT FROM tempCursor 
    INTO @name

END 
select DATEDIFF(ms, @startTime, GETDATE())
CLOSE tempCursor
DEALLOCATE tempCursor

ROLLBACK 
DROP TABLE #temp

la mise à jour prend 156 ms et le curseur, 2016 ms.

0
Mladen Prajdic