Database
 sql >> Datenbank >  >> RDS >> Database

Ersetzen von SQL-Cursor durch Alternativen zur Vermeidung von Leistungsproblemen

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:

  1. Cursor deklarieren
  2. Cursor öffnen
  3. Zeilen abrufen
  4. Cursor schließen
  5. 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:

  1. Wir müssen ‚_Backup‘ zu den Namen aller existierenden Tabellen in einer Datenbank hinzufügen
  2. 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:

  1. Deklarieren Sie eine Tabellenvariable
  2. Tabellennamen und IDs in der von uns deklarierten Tabellenvariable speichern
  3. Setzen Sie den Zähler auf 1 und erhalten Sie die Gesamtzahl der Datensätze aus der Tabellenvariable
  4. Verwenden Sie eine „while“-Schleife, solange der Zähler kleiner oder gleich der Gesamtzahl der Datensätze ist
  5. 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:

  1. Deklarieren Sie eine temporäre Tabelle
  2. Tabellennamen und IDs in der temporären Tabelle speichern, die wir gerade deklariert haben
  3. Setzen Sie den Zähler auf 1 und erhalten Sie die Gesamtzahl der Datensätze aus der temporären Tabelle
  4. Verwenden Sie eine „while“-Schleife, solange der Zähler kleiner oder gleich der Gesamtzahl der Datensätze ist
  5. 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:

  1. Indizes aller Tabellen in einer Beispieldatenbank erstellen und umbenennen – zuerst über einen Cursor und dann mit alternativen Methoden (Tabellenvariablen und temporäre Tabellen)
  2. Setzen Sie die Namen der Tabellen aus diesem Artikel mit alternativen Methoden (temporäre Tabellen und Tabellenvariablen) auf ihre ursprünglichen Namen zurück
  3. 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