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

Sostituzione dei cursori SQL con alternative per evitare problemi di prestazioni

In questo articolo, esamineremo alcune alternative all'utilizzo dei cursori SQL che possono aiutare a evitare problemi di prestazioni causati dall'utilizzo dei cursori.

Prima di discutere le alternative, esaminiamo il concetto generale di cursori SQL.

Veloce panoramica dei cursori SQL

I cursori SQL vengono utilizzati principalmente quando le operazioni basate su insiemi non sono applicabili ed è necessario accedere ai dati ed eseguire operazioni una riga alla volta anziché applicare una singola operazione basata su insiemi a un intero oggetto (come una tabella o un insieme di tabelle).

Definizione semplice

Un cursore SQL fornisce l'accesso ai dati una riga alla volta, offrendoti così un controllo diretto riga per riga sul set di risultati.

Definizione Microsoft

Secondo la documentazione Microsoft, le istruzioni di Microsoft SQL Server producono un set di risultati completo, ma a volte è meglio elaborarlo una riga alla volta, operazione che può essere eseguita aprendo un cursore sul set di risultati.

Il processo in 5 fasi dell'utilizzo di un cursore

Il processo di utilizzo di un cursore SQL può essere generalmente descritto come segue:

  1. Dichiara cursore
  2. Apri cursore
  3. Recupera righe
  4. Chiudi cursore
  5. Dealloca cursore

Nota importante

Tieni presente che, secondo Vaidehi Pandere, i cursori sono puntatori che occupano la memoria di sistema, che altrimenti sarebbe riservata ad altri processi importanti. Ecco perché l'attraversamento di un set di risultati di grandi dimensioni utilizzando i cursori di solito non è l'idea migliore, a meno che non ci sia un motivo legittimo per farlo.

Per informazioni più dettagliate su questo, non esitare a fare riferimento al mio articolo Come utilizzare i cursori SQL per scopi speciali.

Esempio cursore SQL

Per prima cosa, esamineremo un esempio di come è possibile utilizzare un cursore SQL per rinominare gli oggetti del database uno per uno.

Per creare un cursore SQL di cui abbiamo bisogno, impostiamo un database di esempio in modo da poter eseguire i nostri script su di esso.

Imposta database di esempio (UniversityV3)

Eseguire lo script seguente per creare e popolare il database di esempio di UniversityV3 con due tabelle:

-- (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

Crea un cursore SQL per rinominare le tabelle (_Backup)

Ora considera di soddisfare le seguenti specifiche utilizzando un cursore:

  1. Dobbiamo aggiungere '_Backup' ai nomi di tutte le tabelle esistenti in un database
  2. Le tabelle che hanno già "_Backup" nel nome non devono essere rinominate

Creiamo un cursore SQL per rinominare tutte le tabelle nel database di esempio aggiungendo '_Backup' al nome di ogni tabella assicurandoci anche che le tabelle contenenti '_Backup' nel loro nome non vengano rinominate di nuovo eseguendo il codice seguente:

-- 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

Esegui lo script di ridenominazione e visualizza i risultati

Ora, premi F5 in SSMS (SQL Server Management Studio) per eseguire lo script e vedere i risultati:

L'aggiornamento dei nomi delle tabelle in SSMS Object Explorer mostra chiaramente che le abbiamo modificate correttamente come specificato.

Eseguiamo nuovamente lo script premendo nuovamente F5 e osserviamo i risultati:

Creazione di un cursore SQL per reimpostare _Nome backup

Dobbiamo anche creare uno script che utilizzi un cursore SQL per riportare i nomi delle tabelle che abbiamo appena cambiato a quelli iniziali:lo faremo rimuovendo '_Backup' dai loro nomi.

Lo script qui sotto ci permetterà di fare proprio questo :

-- 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

Esegui lo script di ripristino e visualizza i risultati

L'esecuzione dello script mostra che i nomi delle tabelle sono stati reimpostati correttamente:

Questi sono stati gli esempi di alcuni scenari in cui è difficile evitare di utilizzare i cursori SQL a causa della natura del requisito. Tuttavia, è ancora possibile trovare un approccio alternativo.

Alternative al cursore SQL

Esistono due alternative più comuni per i cursori SQL, quindi esaminiamo ciascuna di esse in dettaglio.

Alternativa 1:variabili di tabella

Una di queste alternative sono le variabili di tabella.

Le variabili di tabella, proprio come le tabelle, possono memorizzare più risultati, ma con alcune limitazioni. Secondo la documentazione Microsoft, una variabile di tabella è un tipo di dati speciale utilizzato per memorizzare un set di risultati per l'elaborazione in un secondo momento.

Tuttavia, tieni presente che le variabili di tabella vengono utilizzate al meglio con piccoli set di dati.

Le variabili di tabella possono essere molto efficienti per le query su piccola scala poiché funzionano come variabili locali e vengono ripulite automaticamente quando escono dall'ambito.

Strategia per la variabile tabella:

Useremo le variabili di tabella invece dei cursori SQL per rinominare tutte le tabelle da un database seguendo questi passaggi:

  1. Dichiara una variabile di tabella
  2. Memorizza i nomi e gli ID delle tabelle nella variabile di tabella che abbiamo dichiarato
  3. Imposta il contatore su 1 e ottieni il numero totale di record dalla variabile tabella
  4. Utilizza un ciclo "while" purché il contatore sia inferiore o uguale al numero totale di record
  5. All'interno del ciclo 'while', rinomineremo le tabelle una per una purché non siano già rinominate e aumenteremo il contatore per ogni tabella

Codice variabile tabella:

Esegui il seguente script SQL che crea e utilizza una variabile di tabella per rinominare le tabelle:

-- 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

Esegui lo script e visualizza i risultati

Ora eseguiamo lo script e controlliamo i risultati:

Alternativa 2:Tabelle Temporanee

Possiamo anche usare tabelle temporanee invece di cursori SQL per iterare il set di risultati una riga alla volta.

Le tabelle temporanee sono in uso da molto tempo e forniscono un ottimo modo per sostituire i cursori per grandi set di dati.

Proprio come le variabili di tabella, le tabelle temporanee possono contenere il set di risultati in modo che possiamo eseguire le operazioni necessarie elaborandolo con un algoritmo iterativo come un ciclo "while".

Strategia del tavolo temporaneo:

Useremo una tabella temporanea per rinominare tutte le tabelle nel database di esempio seguendo questi passaggi:

  1. Dichiara una tabella temporanea
  2. Memorizza i nomi e gli ID delle tabelle nella tabella temporanea che abbiamo appena dichiarato
  3. Imposta il contatore su 1 e ottieni il numero totale di record dalla tabella temporanea
  4. Utilizza un ciclo "while" purché il contatore sia inferiore o uguale al numero totale di record
  5. All'interno del ciclo 'while', rinomina le tabelle una per una purché non siano già rinominate e aumenta il contatore per ciascuna tabella

Reimposta le tabelle

Dobbiamo ripristinare i nomi delle tabelle alla loro forma iniziale eliminando '_Backup' dalla fine dei loro nomi, quindi esegui nuovamente lo script di ripristino che abbiamo già scritto e utilizzato sopra in modo da poter applicare un altro metodo per rinominare le tabelle.

Codice tabella temporanea:

Esegui il seguente script SQL per creare e utilizzare una tabella temporanea per rinominare tutte le tabelle nel nostro database:

-- 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

Esegui lo script e controlla l'output

Ora eseguiamo lo script per visualizzare i risultati:

Cose da fare

Ora che hai familiarità con le alternative ai cursori SQL, come l'utilizzo di variabili di tabella e tabelle temporanee, prova a eseguire le seguenti operazioni per acquisire dimestichezza con l'applicazione pratica di questa conoscenza:

  1. Crea e rinomina gli indici di tutte le tabelle in un database di esempio, prima tramite un cursore e poi utilizzando metodi alternativi (variabili di tabella e tabelle temporanee)
  2. Ripristina i nomi delle tabelle da questo articolo ai loro nomi iniziali utilizzando metodi alternativi (tabelle temporanee e variabili di tabella)
  3. Puoi anche fare riferimento ai primi esempi nel mio articolo Come utilizzare i cursori SQL per scopi speciali e provare a popolare le tabelle con molte righe e misurare le statistiche e il tempo per le query per confrontare il metodo del cursore di base con le alternative