In diesem Artikel sehen wir uns einige Alternativen zur Verwendung von SQL-Cursorn an, die dazu beitragen können, Leistungsprobleme zu vermeiden, die durch die Verwendung von Cursorn verursacht werden.
Bevor wir die Alternativen diskutieren, wollen wir uns das allgemeine Konzept von SQL-Cursorn ansehen.
Kurzübersicht über SQL-Cursor
SQL-Cursor werden hauptsächlich dort verwendet, wo mengenbasierte Operationen nicht anwendbar sind und Sie auf Daten zugreifen und Operationen zeilenweise ausführen müssen, anstatt eine einzelne mengenbasierte Operation auf ein gesamtes Objekt anzuwenden (z. B. eine Tabelle oder eine Menge von Tabellen).
Einfache Definition
Ein SQL-Cursor bietet zeilenweise Zugriff auf Daten, wodurch Sie die Ergebnismenge zeilenweise direkt steuern können.
Microsoft-Definition
Gemäß der Microsoft-Dokumentation erzeugen Microsoft SQL Server-Anweisungen eine vollständige Ergebnismenge, aber es gibt Zeiten, in denen es am besten ist, sie zeilenweise zu verarbeiten – was durch Öffnen eines Cursors auf der Ergebnismenge erreicht werden kann.
Der 5-Schritte-Prozess zur Verwendung eines Cursors
Der Prozess der Verwendung eines SQL-Cursors kann allgemein wie folgt beschrieben werden:
- Cursor deklarieren
- Cursor öffnen
- Zeilen abrufen
- Cursor schließen
- Zuweisung des Cursors aufheben
Wichtiger Hinweis
Bitte bedenken Sie, dass Cursor laut Vaidehi Pandere Zeiger sind, die Ihren Systemspeicher belegen – der sonst für andere wichtige Prozesse reserviert wäre. Aus diesem Grund ist es normalerweise nicht die beste Idee, eine große Ergebnismenge mit Cursorn zu durchlaufen – es sei denn, es gibt einen legitimen Grund dafür.
Ausführlichere Informationen hierzu finden Sie in meinem Artikel How to use SQL Cursors for Special Purposes.
SQL-Cursor-Beispiel
Zuerst sehen wir uns ein Beispiel an, wie ein SQL-Cursor verwendet werden kann, um Datenbankobjekte einzeln umzubenennen.
Um einen benötigten SQL-Cursor zu erstellen, richten wir eine Beispieldatenbank ein, damit wir unsere Skripte darauf ausführen können.
Beispieldatenbank einrichten (UniversityV3)
Führen Sie das folgende Skript aus, um die UniversityV3-Beispieldatenbank mit zwei Tabellen zu erstellen und zu füllen:
-- (1) Create UniversityV3 sample database CREATE DATABASE UniversityV3; GO USE UniversityV3 -- (2) Create Course table IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='Course') DROP TABLE dbo.Course CREATE TABLE [dbo].[Course] ( [CourseId] INT IDENTITY (1, 1) NOT NULL, [Name] VARCHAR (30) NOT NULL, [Detail] VARCHAR (200) NULL, CONSTRAINT [PK_Course] PRIMARY KEY CLUSTERED ([CourseId] ASC) ); -- (3) Create Student table IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES T WHERE T.TABLE_NAME='Student') DROP TABLE dbo.Student CREATE TABLE [dbo].[Student] ( [StudentId] INT IDENTITY (1, 1) NOT NULL, [Name] VARCHAR (30) NULL, [Course] VARCHAR (30) NULL, [Marks] INT NULL, [ExamDate] DATETIME2 (7) NULL, CONSTRAINT [PK_Student] PRIMARY KEY CLUSTERED ([StudentId] ASC) ); -- (4) Populate Course table SET IDENTITY_INSERT [dbo].[Course] ON INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (1, N'DevOps for Databases', N'This is about DevOps for Databases') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (2, N'Power BI Fundamentals', N'This is about Power BI Fundamentals') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (3, N'T-SQL Programming', N'About T-SQL Programming') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (4, N'Tabular Data Modeling', N'This is about Tabular Data Modeling') INSERT INTO [dbo].[Course] ([CourseId], [Name], [Detail]) VALUES (5, N'Analysis Services Fundamentals', N'This is about Analysis Services Fundamentals') SET IDENTITY_INSERT [dbo].[Course] OFF -- (5) Populate Student table SET IDENTITY_INSERT [dbo].[Student] ON INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (1, N'Asif', N'Database Management System', 80, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (2, N'Peter', N'Database Management System', 85, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (3, N'Sam', N'Database Management System', 85, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (4, N'Adil', N'Database Management System', 85, N'2016-01-01 00:00:00') INSERT INTO [dbo].[Student] ([StudentId], [Name], [Course], [Marks], [ExamDate]) VALUES (5, N'Naveed', N'Database Management System', 90, N'2016-01-01 00:00:00') SET IDENTITY_INSERT [dbo].[Student] OFF
Erstellen Sie einen SQL-Cursor zum Umbenennen von Tabellen (_Backup)
Erwägen Sie nun, die folgende Spezifikation zu erfüllen, indem Sie einen Cursor verwenden:
- Wir müssen ‚_Backup‘ zu den Namen aller existierenden Tabellen in einer Datenbank hinzufügen
- Tabellen, die bereits „_Backup“ in ihrem Namen haben, sollten nicht umbenannt werden
Lassen Sie uns einen SQL-Cursor erstellen, um alle Tabellen in der Beispieldatenbank umzubenennen, indem wir „_Backup“ zum Namen jeder Tabelle hinzufügen und gleichzeitig sicherstellen, dass Tabellen, die „_Backup“ in ihrem Namen enthalten, nicht erneut umbenannt werden, indem Sie den folgenden Code ausführen:
-- Declaring the Student cursor to rename all tables by adding ‘_backup’ to their names and also making sure that all tables that are already named correctly will be skipped: USE UniversityV3 GO DECLARE @TableName VARCHAR(50) -- Existing table name ,@NewTableName VARCHAR(50) -- New table name DECLARE Student_Cursor CURSOR FOR SELECT T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor INTO @TableName WHILE @@FETCH_STATUS = 0 BEGIN IF RIGHT(@TableName,6)<>'Backup' -- If Backup table does not exist then rename the table BEGIN SET @[email protected]+'_Backup' -- Add _Backup to the table’s current name EXEC sp_rename @TableName,@NewTableName -- Rename table as OLD table END ELSE PRINT 'Backup table name already exists: '[email protected] FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it in variables INTO @TableName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
Führen Sie das Umbenennungsskript aus und sehen Sie sich die Ergebnisse an
Drücken Sie nun F5 in SSMS (SQL Server Management Studio), um das Skript auszuführen und die Ergebnisse anzuzeigen:
Das Aktualisieren der Tabellennamen im SSMS-Objekt-Explorer zeigt deutlich, dass wir sie wie angegeben erfolgreich geändert haben.
Lassen Sie uns das Skript erneut ausführen, indem Sie erneut F5 drücken, und sehen Sie sich die Ergebnisse an:
Erstellen eines SQL-Cursors zum Zurücksetzen von _Backup Naming
Wir müssen auch ein Skript erstellen, das einen SQL-Cursor verwendet, um die Namen der Tabellen, die wir gerade geändert haben, auf die ursprünglichen zurückzusetzen – wir tun dies, indem wir „_Backup“ aus ihren Namen entfernen.
Mit dem folgenden Skript können wir genau das tun:
-- Declare the Student cursor to reset tables names _backup to their original forms by removing ‘_backup’ USE UniversityV3 GO DECLARE @TableName VARCHAR(50) -- Existing table name ,@NewTableName VARCHAR(50) -- New table name DECLARE Student_Cursor CURSOR FOR SELECT T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor INTO @TableName WHILE @@FETCH_STATUS = 0 BEGIN IF RIGHT(@TableName,6)='Backup' -- If Backup table name exists then reset (rename) it BEGIN SET @NewTableName=SUBSTRING(@TableName,1,LEN(@TableName)-7) -- Remove _Backup from the table name EXEC sp_rename @TableName,@NewTableName -- Rename table END ELSE PRINT 'Backup table name already reset: '[email protected] FETCH NEXT FROM Student_Cursor – Get the data of the next row into cursor and store it in variables INTO @TableName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
Führen Sie das Reset-Skript aus und sehen Sie sich die Ergebnisse an
Das Ausführen des Skripts zeigt, dass die Tabellennamen erfolgreich zurückgesetzt wurden:
Dies waren Beispiele für einige Szenarien, in denen es aufgrund der Art der Anforderung schwierig ist, die Verwendung von SQL-Cursorn zu vermeiden. Es ist jedoch immer noch möglich, einen alternativen Ansatz zu finden.
SQL-Cursor-Alternativen
Es gibt zwei gebräuchlichste Alternativen für SQL-Cursor, also schauen wir uns jede von ihnen im Detail an.
Alternative 1:Tabellenvariablen
Eine dieser Alternativen sind Tabellenvariablen.
Tabellenvariablen können genau wie Tabellen mehrere Ergebnisse speichern – jedoch mit einigen Einschränkungen. Gemäß der Microsoft-Dokumentation ist eine Tabellenvariable ein spezieller Datentyp, der verwendet wird, um eine Ergebnismenge für eine spätere Verarbeitung zu speichern.
Beachten Sie jedoch, dass Tabellenvariablen am besten mit kleinen Datensätzen verwendet werden.
Tabellenvariablen können für kleinere Abfragen sehr effizient sein, da sie wie lokale Variablen funktionieren und automatisch bereinigt werden, wenn sie den Geltungsbereich verlassen.
Strategie für Tabellenvariablen:
Wir werden Tabellenvariablen anstelle von SQL-Cursor verwenden, um alle Tabellen einer Datenbank umzubenennen, indem wir diesen Schritten folgen:
- Deklarieren Sie eine Tabellenvariable
- Tabellennamen und IDs in der von uns deklarierten Tabellenvariable speichern
- Setzen Sie den Zähler auf 1 und erhalten Sie die Gesamtzahl der Datensätze aus der Tabellenvariable
- Verwenden Sie eine „while“-Schleife, solange der Zähler kleiner oder gleich der Gesamtzahl der Datensätze ist
- Innerhalb der „while“-Schleife benennen wir die Tabellen eine nach der anderen um, solange sie nicht bereits umbenannt sind, und erhöhen den Zähler für jede Tabelle
Tabellenvariablencode:
Führen Sie das folgende SQL-Skript aus, das eine Tabellenvariable zum Umbenennen von Tabellen erstellt und verwendet:
-- Declare Student Table Variable to rename all tables by adding ‘_backup’ t their name and also making sure that already renamed tables are skipped USE UniversityV3 GO DECLARE @TableName VARCHAR(50) -- Existing table name ,@NewTableName VARCHAR(50) -- New table name DECLARE @StudentTableVar TABLE -- Declaring a table variable to store tables names ( TableId INT, TableName VARCHAR(40)) INSERT INTO @StudentTableVar -- insert tables names into the table variable SELECT ROW_NUMBER() OVER(ORDER BY T.TABLE_NAME),T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T DECLARE @TotalRows INT=(SELECT COUNT(*) FROM @StudentTableVar),@i INT=1 -- Get total rows and set counter to 1 WHILE @i<[email protected] -- begin as long as i (counter) is less than or equal to the total number of records BEGIN -- ‘While’ loop begins here SELECT @TableName=TableName from @StudentTableVar WHERE [email protected] IF RIGHT(@TableName,6)<>'Backup' -- If a Backup table does not exist, then rename the table BEGIN SET @[email protected]+'_Backup' -- Add _Backup to the table’s current name EXEC sp_rename @TableName,@NewTableName -- Rename the table as OLD table END ELSE PRINT 'Backup table name already exists: '[email protected] SET @[email protected]+1 END -- 'While' loop ends here
Skript ausführen und Ergebnisse anzeigen
Lassen Sie uns nun das Skript ausführen und die Ergebnisse überprüfen:
Alternative 2:Temporäre Tabellen
Wir können auch temporäre Tabellen anstelle von SQL-Cursor verwenden, um die Ergebnismenge zeilenweise zu durchlaufen.
Temporäre Tabellen werden seit langem verwendet und bieten eine hervorragende Möglichkeit, Cursor für große Datenmengen zu ersetzen.
Genau wie Tabellenvariablen können temporäre Tabellen die Ergebnismenge enthalten, sodass wir die erforderlichen Operationen ausführen können, indem wir sie mit einem Iterationsalgorithmus wie einer „while“-Schleife verarbeiten.
Temporäre Tabellenstrategie:
Wir werden eine temporäre Tabelle verwenden, um alle Tabellen in der Beispieldatenbank umzubenennen, indem wir die folgenden Schritte ausführen:
- Deklarieren Sie eine temporäre Tabelle
- Tabellennamen und IDs in der temporären Tabelle speichern, die wir gerade deklariert haben
- Setzen Sie den Zähler auf 1 und erhalten Sie die Gesamtzahl der Datensätze aus der temporären Tabelle
- Verwenden Sie eine „while“-Schleife, solange der Zähler kleiner oder gleich der Gesamtzahl der Datensätze ist
- Benennen Sie innerhalb der „while“-Schleife Tabellen einzeln um, solange sie nicht bereits umbenannt sind, und erhöhen Sie den Zähler für jede Tabelle
Setzen Sie die Tabellen zurück
Wir müssen die Namen der Tabellen auf ihre ursprüngliche Form zurücksetzen, indem wir '_Backup' am Ende ihrer Namen löschen, also führen Sie bitte das Reset-Skript, das wir bereits geschrieben und oben verwendet haben, erneut aus, damit wir eine andere Methode zum Umbenennen von Tabellen anwenden können.
Temporärer Tabellencode:
Führen Sie das folgende SQL-Skript aus, um eine temporäre Tabelle zu erstellen und zu verwenden, um alle Tabellen in unserer Datenbank umzubenennen:
-- Declare the Student Temporary Table to rename all tables by adding ‘_backup’ to their names while also making sure that already renamed tables are skipped USE UniversityV3 GO DECLARE @TableName VARCHAR(50) -- Existing table name ,@NewTableName VARCHAR(50) -- New table name CREATE TABLE #Student -- Declaring a temporary table ( TableId INT, TableName VARCHAR(40) ) INSERT INTO #Student -- insert tables names into the temporary table SELECT ROW_NUMBER() OVER(ORDER BY T.TABLE_NAME),T.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES T DECLARE @TotalRows INT=(SELECT COUNT(*) FROM #Student),@i INT=1 -- Get the total amount of rows and set the counter to 1 WHILE @i<[email protected] -- begin as long as i (counter) is less than or equal to the total number of records BEGIN -- ‘While’ loop begins here SELECT @TableName=TableName from #Student WHERE [email protected] IF RIGHT(@TableName,6)<>'Backup' -- If a Backup table does not exist, then rename the table BEGIN SET @[email protected]+'_Backup' -- Add ‘_Backup’ to the table’s current name EXEC sp_rename @TableName,@NewTableName -- Rename the table as OLD table END ELSE PRINT 'Backup table name already exists: '[email protected] SET @[email protected]+1 END -- While loop ends here DROP TABLE #Student
Führen Sie das Skript aus und überprüfen Sie die Ausgabe
Lassen Sie uns nun das Skript ausführen, um die Ergebnisse anzuzeigen:
Dinge zu tun
Nachdem Sie nun mit Alternativen zu SQL-Cursorn vertraut sind – wie der Verwendung von Tabellenvariablen und temporären Tabellen – versuchen Sie bitte Folgendes, um sich mit der praktischen Anwendung dieses Wissens vertraut zu machen:
- Indizes aller Tabellen in einer Beispieldatenbank erstellen und umbenennen – zuerst über einen Cursor und dann mit alternativen Methoden (Tabellenvariablen und temporäre Tabellen)
- Setzen Sie die Namen der Tabellen aus diesem Artikel mit alternativen Methoden (temporäre Tabellen und Tabellenvariablen) auf ihre ursprünglichen Namen zurück
- Sie können sich auch auf die ersten Beispiele in meinem Artikel How to use SQL Cursors for Special Purposes beziehen und versuchen, Tabellen mit vielen Zeilen zu füllen und die Statistiken und die Zeit für die Abfragen zu messen, um die grundlegende Cursormethode mit den Alternativen zu vergleichen