it-swarm.com.de

Warum hassen die Leute SQL-Cursor so sehr?

Ich kann verstehen, dass ich aufgrund des Overheads und der Unannehmlichkeiten die Verwendung eines Cursors vermeiden möchte, aber es sieht so aus, als würde eine ernsthafte Cursor-Phobie-Manie stattfinden, bei der die Leute große Anstrengungen unternehmen, um die Verwendung eines Cursors zu vermeiden.

In einer Frage wurde beispielsweise die Frage gestellt, wie mit einem Cursor etwas offensichtlich Triviales zu tun ist, und die akzeptierte Antwort unter Verwendung einer rekursiven Abfrage mit einem allgemeinen Tabellenausdruck (Common Table Expression, CTE) mit einer rekursiven benutzerdefinierten Funktion vorgeschlagen, obwohl dadurch die Anzahl der Zeilen, die verarbeitet werden können, auf 32 begrenzt wird (aufgrund des rekursiven Funktionsaufruflimits in SQL Server). Dies scheint mir eine schreckliche Lösung für die Systemlebensdauer zu sein, ganz zu schweigen von einem enormen Aufwand, nur um die Verwendung eines einfachen Cursors zu vermeiden.

Was ist der Grund für diesen wahnsinnigen Hass? Hat eine 'notierte Autorität' eine Fatwa gegen Cursor herausgegeben? Verbirgt sich ein unaussprechliches Übel im Herzen von Cursorn, die die Moral von Kindern verderben oder so?

Wiki-Frage, mehr interessiert an der Antwort als an der Vertretung.

Verwandte Informationen:

SQL Server-Schnellvorlauf-Cursor

EDIT: Lassen Sie mich genauer sein: Ich verstehe, dass Cursor nicht anstelle von normalen relationalen Operationen verwendet werden sollten ; das ist ein Kinderspiel. Was ich nicht verstehe, ist, dass die Leute alles tun, um Cursorn wie Cooties oder Ähnliches zu vermeiden, selbst wenn ein Cursor eine einfachere und/oder effizientere Lösung ist. Es ist der irrationale Hass, der mich verblüfft, nicht die offensichtliche technische Effizienz.

126
Steven A. Lowe

Der "Overhead" mit Cursorn ist nur ein Teil der API. Cursor sind die Funktionsweise von Teilen des RDBMS unter der Haube. Häufig CREATE TABLE und INSERT haben SELECT Anweisungen, und die Implementierung ist die offensichtliche interne Cursor-Implementierung.

Durch die Verwendung von "satzbasierten Operatoren" auf höherer Ebene werden die Cursorergebnisse in einer einzigen Ergebnismenge zusammengefasst, was weniger Hin- und Herbewegungen der API bedeutet.

Cursor sind älter als moderne Sprachen und bieten erstklassige Sammlungen. Old C, COBOL, Fortran usw. mussten die Zeilen einzeln verarbeiten, da der Begriff "Sammlung" nicht allgemein verwendet werden konnte. Java, C #, Python usw. verfügen über erstklassige Listenstrukturen für Ergebnismengen.

The Slow Issue

In einigen Kreisen sind die relationalen Verknüpfungen ein Rätsel, und die Leute werden verschachtelte Cursor schreiben und keine einfachen Verknüpfungen. Ich habe wirklich epische Nested-Loop-Operationen gesehen, die als sehr viele Cursor geschrieben wurden. Eine RDBMS-Optimierung umgehen. Und läuft wirklich langsam.

Einfache SQL-Umschreibungen, um verschachtelte Cursor-Schleifen durch Joins zu ersetzen, und eine einzelne flache Cursor-Schleife können dazu führen, dass Programme in 100-facher Zeit ausgeführt werden. [Sie dachten, ich sei der Gott der Optimierung. Ich habe nur verschachtelte Schleifen durch Joins ersetzt. Noch verwendete Cursor.]

Diese Verwirrung führt häufig zu einer Anklage gegen Cursor. Es ist jedoch nicht der Cursor, sondern der Missbrauch des Cursors, der das Problem darstellt.

The Size Issue

Für wirklich epische Ergebnismengen (d. H. Das Speichern einer Tabelle in einer Datei) sind Cursor unerlässlich. Die satzbasierten Operationen können keine wirklich großen Ergebnismengen als einzelne Sammlung im Speicher materialisieren.

Alternativen

Ich versuche, so viel wie möglich eine ORM-Ebene zu verwenden. Das hat aber zwei Zwecke. Zunächst werden die Cursor von der ORM-Komponente verwaltet. Zweitens wird die SQL von der Anwendung in eine Konfigurationsdatei getrennt. Es ist nicht so, dass die Cursor schlecht sind. Es ist so, dass das Codieren all dieser Öff- nungen, Schließungen und Abrufe keine wertschöpfende Programmierung ist.

73
S.Lott

Mit Hilfe von Cursorn wenden die Benutzer eine prozedurale Denkweise übermäßig auf eine satzbasierte Umgebung an.

Und sie sind [~ # ~] langsam [~ # ~] !!!

From SQLTeam :

Bitte beachten Sie, dass Cursor die langsamste Möglichkeit sind, auf Daten in SQL Server zuzugreifen. Das sollte nur verwendet werden, wenn Sie wirklich auf eine Zeile gleichzeitig zugreifen müssen. Der einzige Grund, an den ich denken kann, ist, eine gespeicherte Prozedur für jede Zeile aufzurufen. Im Cursor Performance article habe ich festgestellt, dass Cursor über dreißigmal langsamer sind als satzbasierte Alternativen.

41
Galwegian

Die Antwort oben lautet: "Cursor sind die LANGSAMSTE Möglichkeit, auf Daten in SQL Server zuzugreifen ... Cursor sind über dreißig Mal langsamer als satzbasierte Alternativen."

Diese Aussage mag unter vielen Umständen zutreffen, ist jedoch als pauschale Aussage problematisch. Zum Beispiel habe ich Cursor in Situationen eingesetzt, in denen ich eine Aktualisierungs- oder Löschoperation ausführen möchte, die viele Zeilen einer großen Tabelle betrifft, die konstante Produktionslesevorgänge empfängt. Das Ausführen einer gespeicherten Prozedur, die diese Aktualisierungen zeilenweise ausführt, ist schneller als satzbasierte Vorgänge, da der satzbasierte Vorgang mit dem Lesevorgang in Konflikt steht und entsetzliche Sperrprobleme verursacht (und das Produktionssystem möglicherweise vollständig abstürzt). in Extremfällen).

In Abwesenheit anderer Datenbankaktivitäten sind satzbasierte Vorgänge allgemein schneller. In Produktionssystemen kommt es darauf an.

19
davidcl

Cursor werden in der Regel von Anfängern von SQL-Entwicklern an Stellen verwendet, an denen satzbasierte Vorgänge besser wären. Insbesondere wenn die Leute SQL lernen, nachdem sie eine traditionelle Programmiersprache gelernt haben, führt die Mentalität "über diese Datensätze iterieren" dazu, dass die Leute Cursor unangemessen verwenden.

Zu den wichtigsten SQL-Büchern gehört ein Kapitel über die Verwendung von Cursorn. Gut geschriebene machen deutlich, dass Cursor ihren Platz haben, aber nicht für satzbasierte Operationen verwendet werden sollten.

Es gibt offensichtlich Situationen, in denen Cursor die richtige Wahl sind oder zumindest eine richtige Wahl.

9
davidcl

Der Optimierer kann die relationale Algebra häufig nicht verwenden, um das Problem zu transformieren, wenn eine Cursormethode verwendet wird. Oft ist ein Cursor eine großartige Möglichkeit, ein Problem zu lösen, aber SQL ist eine deklarative Sprache, und die Datenbank enthält viele Informationen, von Einschränkungen bis hin zu Statistiken und Indizes, sodass der Optimierer viele Optionen zur Lösung des Problems hat Problem, während ein Cursor so ziemlich explizit die Lösung leitet.

9
Cade Roux

In Oracle PL/SQL führen Cursor nicht zu Tabellensperren, und es ist möglich, Bulk-Collecting/Bulk-Fetching zu verwenden.

In Oracle 10 der häufig verwendete implizite Cursor

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

ruft implizit 100 Zeilen gleichzeitig ab. Explizites Sammeln/Holen von Massengut ist ebenfalls möglich.

PL/SQL-Cursor sind jedoch ein letzter Ausweg. Verwenden Sie sie, wenn Sie ein Problem mit satzbasiertem SQL nicht lösen können.

Ein weiterer Grund ist die Parallelisierung. Für die Datenbank ist es einfacher, große satzbasierte Anweisungen als zeilenweisen Imperativcode zu parallelisieren. Aus demselben Grund wird die funktionale Programmierung immer beliebter (Haskell, F #, LISP, C # LINQ, MapReduce ...). Die funktionale Programmierung erleichtert die Parallelisierung. Die Anzahl der CPUs pro Computer steigt, sodass die Parallelisierung immer mehr zum Problem wird.

8
tuinstoel

Die obigen Antworten haben die Wichtigkeit des Sperrens nicht genug betont. Ich bin kein großer Fan von Cursorn, da sie häufig zu Sperren auf Tabellenebene führen.

6
Richard T

Im Allgemeinen ist die Leistung von Code unter Verwendung von Cursorn in einer relationalen Datenbank um eine Größenordnung schlechter als bei satzbasierten Operationen.

6
Charles Bretana

Für das, was es wert ist, habe ich gelesen, dass die "eine" Stelle, an der ein Cursor auftaucht, sein satzbasiertes Gegenstück in einer laufenden Summe darstellt. Bei einer kleinen Tabelle begünstigt die Geschwindigkeit, mit der die Zeilen über die Reihenfolge nach Spalten aufsummiert werden, die satzbasierte Operation. Mit zunehmender Zeilengröße wird der Cursor jedoch schneller, da er den laufenden Gesamtwert einfach zum nächsten Durchgang der Tabelle übertragen kann Schleife. Nun wo Sie sollten eine laufende Summe machen, ist ein anderes Argument ...

3
Eric Sabine

Abgesehen von den (Nicht-) Leistungsproblemen denke ich, dass das größte Versagen von Cursorn darin besteht, dass sie schmerzhaft zu debuggen sind. Insbesondere im Vergleich zu Code in den meisten Clientanwendungen, in denen das Debuggen vergleichsweise einfach und die Sprachfunktionen viel einfacher sind. Tatsächlich behaupte ich, dass fast alles, was man in SQL mit einem Cursor macht, wahrscheinlich überhaupt in der Client-App stattfinden sollte.

2
Wyatt Barnett
2
Edin Omeragic

Sie hätten Ihre Frage wahrscheinlich nach dem zweiten Absatz schließen können, anstatt die Leute einfach deshalb als "verrückt" zu bezeichnen, weil sie einen anderen Standpunkt vertreten als Sie, und sonst zu versuchen, Fachleute zu verspotten, die einen sehr guten Grund haben, sich so zu fühlen wie sie.

Was Ihre Frage angeht, gibt es zwar sicherlich Situationen, in denen ein Cursor erforderlich sein kann, aber meiner Erfahrung nach entscheiden Entwickler, dass ein Cursor FERN häufiger verwendet werden muss, als dies tatsächlich der Fall ist. Meiner Meinung nach ist die Wahrscheinlichkeit, dass jemand auf der Seite der zu häufigen Verwendung von Cursorn irrt, VIEL höher.

1
Tom H

Können Sie dieses Cursor-Beispiel posten oder auf die Frage verlinken? Es gibt wahrscheinlich einen noch besseren Weg als einen rekursiven CTE.

Neben anderen Kommentaren verursachen Cursor bei unsachgemäßer Verwendung (was häufig der Fall ist) unnötige Seiten-/Zeilensperren.

1
Gordon Bell

im Grunde genommen 2 Codeblöcke, die dasselbe tun. Vielleicht ist es ein komisches Beispiel, aber es beweist den Punkt. 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

das einzelne Update dauert 156 ms, während der Cursor 2016 ms benötigt.

0
Mladen Prajdic