Frage

Es ist allgemein anerkannt, dass die Verwendung von Cursorn in gespeicherten Prozeduren nach Möglichkeit vermieden werden sollte (sie sollten durch satzbasierte Logik usw. ersetzt werden).Wenn Sie die Fälle annehmen, in denen Sie über einige Daten iterieren müssen und dies schreibgeschützt tun können, sind dann Fast-Forward-Cursor (Nur-Lese-Vorlauf) mehr oder weniger ineffizient als beispielsweise While-Schleifen?Nach meinen Untersuchungen sieht es so aus, als ob die Cursor-Option im Allgemeinen schneller ist und weniger Lese- und CPU-Zeit beansprucht.Ich habe keine umfangreichen Tests durchgeführt, aber finden andere das auch?Bringen Cursor dieser Art (Schnellvorlauf) zusätzlichen Overhead oder Ressourcen mit sich, die teuer sein könnten, von denen ich nichts weiß?

Geht es bei all dem Gerede darüber, keine Cursor zu verwenden, wirklich darum, die Verwendung von Cursorn zu vermeiden, wenn satzbasierte Ansätze verfügbar sind, und aktualisierbare Cursor usw. zu verwenden?

Danke

War es hilfreich?

Lösung

Die „Best Practice“ zur Vermeidung von Cursorn in SQL Server geht auf SQL Server 2000 und frühere Versionen zurück.Durch die Neufassung der Engine in SQL 2005 wurden die meisten Probleme im Zusammenhang mit Cursorproblemen behoben, insbesondere durch die Einführung der Schnellvorlaufoption.Cursor sind nicht unbedingt schlechter als satzbasierte Cursor und werden in Oracle PL/SQL (LOOP) umfassend und erfolgreich eingesetzt.

Das „allgemein anerkannte“, auf das Sie sich beziehen War gültig, aber mittlerweile veraltet und falsch – gehen Sie davon aus, dass sich Schnellvorlauf-Cursor wie angekündigt verhalten und funktionieren.Führen Sie einige Tests und Recherchen durch und stützen Sie Ihre Ergebnisse auf SQL2005 und höher

Andere Tipps

Während ein Schnellvorlauf-Cursor in SQL Server 2005 einige Optimierungen aufweist, ist dies der Fall nicht Es stimmt, dass sie in Bezug auf die Leistung einer satzbasierten Abfrage nahe kommen.Es gibt nur sehr wenige Situationen, in denen die Cursorlogik nicht durch eine satzbasierte Abfrage ersetzt werden kann.Cursor werden von Natur aus immer langsamer sein, was zum Teil daran liegt, dass Sie die Ausführung immer wieder unterbrechen müssen, um Ihre lokalen Variablen zu füllen.

Hier sind einige Referenzen, die nur die Spitze des Eisbergs wären, wenn Sie sich mit diesem Thema befassen:

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

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

Diese Antwort soll die bisher gegebenen Antworten konsolidieren.

1) Wenn möglich, verwenden Sie für Ihre Abfragen eine satzbasierte Logik, d. h.Versuchen Sie es einfach SELECT, INSERT, UPDATE oder DELETE mit dem entsprechenden FROM Klauseln oder verschachtelte Abfragen – diese sind fast immer schneller.

2) Wenn das oben Genannte nicht möglich ist, dann in SQL Server 2005+ FAST FORWARD Cursor sind effizient und leistungsstark und sollten gegenüber While-Schleifen verwendet werden.

„Wenn Sie einen noch schnelleren Cursor als FAST FORWARD wünschen, verwenden Sie einen STATISCHEN Cursor.Sie sind schneller als FAST FORWARD.Nicht extrem schneller, kann aber einen Unterschied machen.“

Nicht so schnell!Laut Microsoft:„Normalerweise wurde bei diesen Konvertierungen der Cursortyp zu einem ‚teureren‘ Cursortyp herabgestuft.Im Allgemeinen ist ein (FAST) FORWARD-ONLY-Cursor am leistungsstärksten, gefolgt von DYNAMIC, KEYSET und schließlich STATIC, der im Allgemeinen am wenigsten leistungsfähig ist.“

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

In den meisten Fällen können Sie Cursor vermeiden, aber manchmal ist es notwendig.

Denken Sie daran, dass FAST_FORWARD DYNAMISCH ist ...FORWARD_ONLY können Sie mit einem STATIC-Cursor verwenden.

Versuchen Sie es bei der Halloween-Aufgabe, um zu sehen, was passiert!!!

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

Die Leute meiden Cursor, weil sie im Allgemeinen schwieriger zu schreiben sind als einfache While-Schleifen. Eine While-Schleife kann jedoch teuer sein, weil Sie ständig Daten aus einer Tabelle auswählen, ob temporär oder nicht.

Mit einem schreibgeschützten Cursor, der schnell vorspulen kann, bleiben die Daten im Speicher und wurden speziell für die Schleife entwickelt.

Dieser Artikel hebt hervor, dass ein durchschnittlicher Cursor 50-mal schneller läuft als eine While-Schleife.

Einige Alternativen zur Verwendung des Cursors:

Während Loops Tempolar-abgeleitete Tabellen zu assoziierten Fallanweisungen mehrere Abfragen häufig zugeordnet sind, können auch Cursoroperationen mit Nicht-Cursor-Techniken erreicht werden.

Wenn Sie sicher sind, dass der Cursor verwendet werden muss, sollte die Anzahl der zu verarbeitenden Datensätze so weit wie möglich reduziert werden.Eine Möglichkeit hierfür besteht darin, die zu verarbeitenden Datensätze zunächst in eine temporäre Tabelle zu übertragen, nicht in die Originaltabelle, sondern in einen Cursor, der die Datensätze in der temporären Tabelle verwendet.Bei Verwendung dieses Pfades wird davon ausgegangen, dass die Anzahl der Datensätze in der temporären Tabelle im Vergleich zur Originaltabelle stark reduziert wurde.Bei weniger Datensätzen ist der Cursor schneller fertig.

Zu den Cursoreigenschaften, die sich auf die Leistung auswirken, gehören:

NUR VORWÄRTS:Unterstützt nur die Weiterleitung des Cursors von der ersten Zeile bis zum Ende mit FETCH NEXT.Sofern sie nicht als KEYSET oder STATIC festgelegt ist, wird die SELECT-Klausel bei jedem Abrufaufruf neu ausgewertet.

STATISCH:Erstellt eine temporäre Kopie der erstellten Daten und wird vom Cursor verwendet.Dadurch wird verhindert, dass der Cursor bei jedem Aufruf neu berechnet wird, was die Leistung verbessert.Dies ermöglicht keine Änderung des Cursortyps und Änderungen an der Tabelle werden beim Aufruf des Abrufs nicht berücksichtigt.

SCHLÜSSELSATZ:Mit dem Cursor versehene Zeilen werden in einer Tabelle unter tempdb platziert, und Änderungen an Nichtschlüsselspalten werden beim Aufruf des Abrufs widergespiegelt.Neu zur Tabelle hinzugefügte Datensätze werden jedoch nicht berücksichtigt.Beim Keyset-Cursor wird die SELECT-Anweisung nicht erneut ausgewertet.

DYNAMISCH:Alle Änderungen an der Tabelle werden im Cursor angezeigt.Der Cursor wird bei jedem Abrufaufruf neu ausgewertet.Es verbraucht viele Ressourcen und beeinträchtigt die Leistung.

FAST_FORWARD:Der Cursor ist unidirektional, z. B. FORWARD_ONLY, gibt den Cursor jedoch als schreibgeschützt an.FORWARD_ONLY ist eine Leistungssteigerung und der Cursor wird nicht bei jedem Abruf neu ausgewertet.Es bietet die beste Leistung, wenn es für die Programmierung geeignet ist.

OPTIMISTISCH:Mit dieser Option können Zeilen im Cursor aktualisiert werden.Wenn eine Zeile abgerufen und aktualisiert wird und eine andere Zeile zwischen Abruf- und Aktualisierungsvorgängen aktualisiert wird, schlägt der Cursoraktualisierungsvorgang fehl.Wenn ein OPTIMISTIC-Cursor verwendet wird, der eine Zeilenaktualisierung durchführen kann, sollte er nicht von einem anderen Prozess aktualisiert werden.

NOTIZ:Wenn Cursore nicht angegeben ist, ist der Standardwert FORWARD_ONLY.

Um Miles ursprüngliche Fragen zu beantworten ...

Schnellvorlauf-, schreibgeschützte, statische Cursor (liebevoll als „Feuerwehrschlauch-Cursor“ bezeichnet) sind in der Regel genauso schnell oder schneller als eine entsprechende temporäre Tabelle und eine While-Schleife, da ein solcher Cursor nichts anderes als eine temporäre Tabelle und eine While-Schleife ist wurde hinter den Kulissen etwas optimiert.

Um das zu ergänzen, was Eric Z.Beard hat in diesem Thread gepostet und um die Frage weiter zu beantworten ...

"Ist alles das Gespräch darüber, Cursors nicht wirklich zu verwenden, um die Verwendung von Cursors zu vermeiden, wenn Set-basierte Ansätze verfügbar sind, und die Verwendung von aktualisierbaren Cursors usw."

Ja.Mit sehr wenigen Ausnahmen benötigt es weniger Zeit und weniger Code, um richtigen satzbasierten Code zu schreiben, der das Gleiche tut wie die meisten Cursor, und hat den zusätzlichen Vorteil, dass er viel weniger Ressourcen verbraucht und in der Regel VIEL schneller läuft als ein Cursor oder eine While-Schleife.Im Allgemeinen und mit Ausnahme bestimmter Verwaltungsaufgaben sollten sie zugunsten eines ordnungsgemäß geschriebenen satzbasierten Codes wirklich vermieden werden.Natürlich gibt es zu jeder „Regel“ Ausnahmen, aber im Fall von Cursorn, While-Schleifen und anderen Formen von RBAR können die meisten Leute die Ausnahmen an einer Hand abzählen, ohne alle Finger zu benutzen.;-)

Es gibt auch den Begriff „Hidden RBAR“.Dies ist Code, der mengenbasiert aussieht, es aber nicht ist.Diese Art von „satzbasiertem“ Code ist der Grund, warum bestimmte Leute RBAR-Methoden angenommen haben und sagen, sie seien „OK“.Beispielsweise ist die Lösung des Problems der laufenden Summe mithilfe einer aggregierten (SUM) korrelierten Unterabfrage mit einer darin enthaltenen Ungleichung zum Erstellen der laufenden Summe meiner Meinung nach nicht wirklich satzbasiert.Stattdessen handelt es sich bei Steroiden um RBAR, da für jede berechnete Zeile wiederholt viele andere Zeilen mit einer Rate von N*(N+1)/2 „berührt“ werden müssen.Dies wird als „Triangular Join“ bezeichnet und ist mindestens halb so schlimm wie ein vollständiger kartesischer Join (Cross Join oder „Square Join“).

Obwohl MS seit SQL Server 2005 einige Verbesserungen an der Funktionsweise von Cursorn vorgenommen hat, ist der Begriff „Schneller Cursor“ im Vergleich zu ordnungsgemäß geschriebenem satzbasierten Code immer noch ein Widerspruch in sich.Das gilt auch in Oracle.Ich habe in der Vergangenheit drei Jahre lang mit Oracle gearbeitet, aber meine Aufgabe bestand darin, Leistungsverbesserungen im vorhandenen Code vorzunehmen.Die meisten der wirklich wesentlichen Verbesserungen wurden realisiert, als ich Cursors in satzbasierten Code konvertierte.Viele Jobs, deren Ausführung früher 4 bis 8 Stunden dauerte, wurden auf Minuten und manchmal Sekunden reduziert.

Wenn Sie einen noch schnelleren Cursor als FAST FORWARD wünschen, verwenden Sie einen STATIC-Cursor.Sie sind schneller als FAST FORWARD.Nicht extrem schneller, kann aber einen Unterschied machen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top