Frage

wusste, dass ich von einigen Performance-Gründen zurück in den SQL 7 Tagen, aber die gleichen Probleme gibt es nach wie vor in SQL Server 2005 tun? Wenn ich einen resultset in einer gespeicherten Prozedur, die ich auf individuell handeln will, sind Cursor immer noch eine schlechte Wahl? Wenn ja, warum?

War es hilfreich?

Lösung

Da Cursor Speicher aufnehmen und Sperren erstellen.

Was Sie wirklich tun Set-basierte Technologie in nicht-Satz basierte Funktionalität zu erzwingen versucht. Und in aller Fairness, sollte ich darauf hinweisen, dass Cursor tun haben eine Anwendung, aber sie sind verpönt, weil viele Leute, die nicht verwendet werden, um unter Verwendung von Set-basierten Lösungen Cursor verwenden, anstatt die Menge herauszufinden, -basierte Lösung.

Aber, wenn Sie einen Cursor öffnen, Sie laden im Grunde die Zeilen in den Speicher und sperrt sie, mögliche Blöcke zu schaffen. Dann, wenn Sie durch den Cursor, werden Sie Änderungen an anderen Tabellen machen und immer noch den gesamten Speicher und Sperren des Cursors offen zu halten.

All das hat das Potenzial, Performance-Probleme für andere Benutzer zu verursachen.

So, als allgemeine Regel, Cursor sind verpönt. Vor allem, wenn das ist die erste Lösung erreichte in der Lösung eines Problems.

Andere Tipps

Die obigen Kommentare über SQL eine Set-basierte Umgebung zu sein sind alle wahr. Allerdings gibt es Zeiten, in denen Zeile-für-Zeile-Operationen geeignet sind. Betrachten wir eine Kombination von Metadaten und dynamischen SQL.

Als einfaches Beispiel, sagen, dass ich mehr als 100 Datensätze in einer Tabelle, die die Namen der Tabellen definieren, die ich kopieren möchten / truncate / was auch immer. Welches das Beste ist? Hartzucodieren die SQL zu tun, was ich brauche? Oder durchlaufen diese resultset und verwenden Dynamic SQL (Sp_executesql), um die Operationen durchführen?

Es gibt keinen Weg, um das oben genannte Ziel mit Set-basiertem SQL zu erreichen.

So verwenden Cursor oder eine while-Schleife (pseudo-Cursor)?

SQL Cursor ist in Ordnung, solange Sie die richtigen Optionen verwenden:

INSENSITIVE wird eine temporäre Kopie Ihrer Ergebnismenge machen (Speichern Sie davon ab, dies selbst für Ihre pseudo-Cursor zu tun).

READ_ONLY wird sicherstellen, dass keine Sperren auf der darunter liegende Ergebnismenge gehalten werden. Veränderungen in der zugrunde liegenden Ergebnismenge werden in der nachfolgenden Fetches reflektiert werden (das gleiche wie wenn TOP bekommen 1 aus dem pseudo-Cursor).

FAST_FORWARD wird eine optimierte vorwärtsgerichteten, schreibgeschützten Cursor erstellen.

Lesen Sie mehr über die verfügbaren Optionen, bevor alle Cursor als böse Entscheidung.

Es ist eine Arbeit, um über Cursor, die ich jedes Mal, wenn ich brauche verwenden.

ich eine Tabellenvariable mit einer Identitätsspalte in ihm.

einfügen alle Daten, mit denen ich in sie arbeiten müssen.

Dann eine Weile Block mit einer Zählvariable machen und wählen Sie die Daten, die ich aus der Tabelle Variable mit einer select-Anweisung will, wo der Identitätsspalt die Zähler übereinstimmt.

So kann ich alles nicht sperren und viel weniger Speicher und ihre sichere Verwendung, werde ich nichts mit einem Speicherfehler oder so ähnlich verlieren.

Und der Block-Code ist leicht zu sehen und zu behandeln.

Dies ist ein einfaches Beispiel:

DECLARE @TAB TABLE(ID INT IDENTITY, COLUMN1 VARCHAR(10), COLUMN2 VARCHAR(10))

DECLARE @COUNT INT,
        @MAX INT, 
        @CONCAT VARCHAR(MAX), 
        @COLUMN1 VARCHAR(10), 
        @COLUMN2 VARCHAR(10)

SET @COUNT = 1

INSERT INTO @TAB VALUES('TE1S', 'TE21')
INSERT INTO @TAB VALUES('TE1S', 'TE22')
INSERT INTO @TAB VALUES('TE1S', 'TE23')
INSERT INTO @TAB VALUES('TE1S', 'TE24')
INSERT INTO @TAB VALUES('TE1S', 'TE25')

SELECT @MAX = @@IDENTITY

WHILE @COUNT <= @MAX BEGIN
    SELECT @COLUMN1 = COLUMN1, @COLUMN2 = COLUMN2 FROM @TAB WHERE ID = @COUNT

    IF @CONCAT IS NULL BEGIN
        SET @CONCAT = '' 
    END ELSE BEGIN 
        SET @CONCAT = @CONCAT + ',' 
    END

    SET @CONCAT = @CONCAT + @COLUMN1 + @COLUMN2

    SET @COUNT = @COUNT + 1
END

SELECT @CONCAT

Ich denke, Cursor einen schlechten Ruf bekommen, weil SQL newbies sie entdecken und denken: „Hey eine for-Schleife! Ich weiß, wie diese nutzen!“ und dann weiter sie sie für alles benutzen.

Wenn Sie sie für verwenden, was sie entworfen wurde, kann ich nicht mit, dass bemängeln.

SQL ist eine Reihe basierte Sprache -. Das ist, was sie am besten kann

Ich denke, Cursor noch eine schlechte Wahl sind, wenn Sie genug über sie zu verstehen, ihre Verwendung unter bestimmten Umständen zu rechtfertigen.

Ein weiterer Grund, warum ich Cursor nicht mag, ist Klarheit. Der Cursorblock ist so hässlich, dass es schwierig ist, in einer klaren und effektiven Art und Weise zu nutzen.

Alles gesagt wurde, es sind einige Fälle, wo ein Cursor wirklich am besten ist - sie sind einfach nicht in der Regel die Fälle, dass Anfänger sie für verwenden möchten

.

Manchmal ist die Art der Verarbeitung müssen Sie erfordert Cursor durchzuführen, wenn auch aus Performance-Gründen ist es immer besser, die Operation zu schreiben (s) unter Verwendung von Set-basierter Logik, wenn möglich.

Ich würde es nicht als „schlechte Praxis“ Cursor zu verwenden, aber sie mehr Ressourcen auf dem Server (als ein äquivalenter mengenbasierten Ansatz) und häufige verbrauchen, als nicht sie sind nicht erforderlich. Da wäre mein Rat andere Optionen zu prüfen, bevor auf einen Cursor zurückzugreifen.

Es gibt mehrere Arten von Cursor (forward-only, statisch, Keyset, dynamisch). Jeder hat unterschiedliche Leistungsmerkmale und die damit verbundenen Aufwand. Stellen Sie sicher, dass Sie den richtigen Cursor-Typ für Ihren Betrieb nutzen. Zukunfts nur ist die Standardeinstellung.

Ein Argument einen Cursor für die Verwendung ist, wenn Sie einzelne Zeilen verarbeiten und aktualisieren müssen, vor allem für eine Datenmenge, die keinen guten eindeutigen Schlüssel hat. In diesem Fall können Sie die FOR UPDATE-Klausel verwenden, wenn die Cursor und Prozess-Updates mit UPDATE erklärt ... WHERE CURRENT OF.

Beachten Sie, dass „serverseitige“ Cursor verwendet, populär zu sein (von ODBC und OLE DB), aber ADO.NET nicht unterstützt, und AFAIK nie.

@ Daniel P -> Sie müssen nicht um einen Cursor zu verwenden, es zu tun. Sie können ganz einfach Satz basierte Theorie, es zu tun verwenden. Zum Beispiel: mit SQL 2008

DECLARE @commandname NVARCHAR(1000) = '';

SELECT @commandname += 'truncate table ' + tablename + '; ';
FROM tableNames;

EXEC sp_executesql @commandname;

wird einfach das tun, was Sie oben gesagt haben. Und Sie können mit SQL 2000 das gleiche zu tun, aber die Syntax der Abfrage wäre anders.

Allerdings ist mein Rat Cursor so weit wie möglich zu vermeiden.

Gayam

Es gibt sehr, sehr wenige Fälle, in denen die Verwendung eines Cursors gerechtfertigt ist. Es gibt so gut wie keine Fälle, in denen es eine relationale, Set-Abfrage übertrifft. Manchmal ist es leichter, dass ein Programmierer in Bezug auf den Schleifen zu denken, aber die Verwendung von Set-Logik, zum Beispiel eine große Anzahl von Zeilen in einer Tabelle zu aktualisieren, wird in einer Lösung führen, dass viele nicht nur weniger Zeilen von SQL-Code, aber das läuft viel schneller, oft um mehrere Größenordnungen schneller.

Auch die Vorspulen Cursor in SQL Server 2005 kann nicht mit Set-basierten Abfragen konkurrieren. Die grafische Darstellung der Leistungsverschlechterung beginnt oft wie ein n ^ 2 Betrieb aussieht im Vergleich zu Set-basierte, die mehr linear zu sein, da die Datenmenge sehr groß wird tendiert.

Cursoren tun hat ihren Platz, aber ich denke, es ist in erster Linie, weil sie häufig verwendet werden, wenn eine einzelne select-Anweisung Aggregation und Filterung der Ergebnisse liefern würde genügen.

Die Vermeidung Cursors ermöglicht SQL Server, um mehr vollständig die Leistung der Abfrage zu optimieren, sehr wichtig in größeren Systemen.

Cursor ist in der Regel nicht die Krankheit, sondern ein Symptom davon. Nicht den Set-basierter Ansatz (wie in den anderen Antworten erwähnt)

Verständnis dieses Problems nicht, und einfach zu glauben, dass die „bösen“ Cursor zu vermeiden es lösen, kann alles noch schlimmer machen.

Beispiel Ersetzen Cursor Iteration durch anderen iterativen Code, wie beispielsweise Daten zur temporären Tabellen oder Tabellenvariablen, einer Schleife über die Zeilen in eine Weise zu bewegen, wie:

SELECT * FROM @temptable WHERE Id=@counter 

oder

SELECT TOP 1 * FROM @temptable WHERE Id>@lastId

Ein solcher Ansatz, wie im Code einer anderen Antwort gezeigt, macht die Dinge viel schlimmer und das ursprüngliche Problem nicht beheben. Es ist ein Anti-Muster genannt Cargo-Kult-Programmierung : nicht zu wissen, warum etwas schlecht ist und damit etwas schlechte Umsetzung es zu vermeiden! Vor kurzem habe ich einen solchen Code (unter Verwendung eines #temptable und keinen Index auf Identität / PK) zurück auf einen Cursor geändert, und etwas mehr als 10000 Zeilen dauerte nur 1 Sekunde statt fast 3 Minuten aktualisiert wird. Noch Set-basierten Ansatz fehlt (das kleinere Übel zu sein), aber das Beste, was ich in diesem Moment tun kann.

Ein weiteres Symptom dieses Unverständnis kann, was sein nenne ich manchmal „ein Objekt Krankheit“: Datenbankanwendungen, die einzelne Objekte durch den Datenzugriff Schichten oder objektrelationalen Mapper behandeln. Typischerweise Code wie:

var items = new List<Item>();
foreach(int oneId in itemIds)
{
    items.Add(dataAccess.GetItemById(oneId);
}

statt

var items = dataAccess.GetItemsByIds(itemIds);

Die erste wird in der Regel die Datenbank mit jeder Menge SELECTs flutet, eine Hin- und Rückfahrt für jeden, vor allem, wenn Objektbäume / Graphen ins Spiel kommen und die berüchtigten SELECT N + 1 Problem schlägt.

Dies ist die Anwendung Seite nicht relationale Datenbanken und Satz basierten Ansatz zu verstehen, sondern nur die gleiche Art und Weise Cursor werden, wenn Verfahren Datenbank-Code, wie T-SQL oder PL / SQL!

Das grundlegende Problem, denke ich, ist, dass Datenbanken konzipiert ist und für Set-basierten Operationen abgestimmt -. Wählt, Aktualisierungen und Löschungen von großen Datenmengen in einem einzigen schnellen Schritt basierend auf Beziehungen in den Daten

In-Memory-Software, auf der anderen Seite, für einzelne Operationen ausgelegt ist, so über einen Satz von Datenschleifen und möglicherweise unterschiedliche Operationen seriell für jedes Element ist, was es am besten.

Looping ist nicht das, was die Datenbank oder Speicherarchitektur sind für und sogar in SQL Server 2005, gehen Sie nicht die Leistung erhalten überall in der Nähe erhalten Sie, wenn Sie die Basisdaten ziehen in ein benutzerdefiniertes Programm gesetzt und tun das im Speicher Looping, Datenobjekte mit / Strukturen, die so leicht wie möglich sind.

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