Database
 sql >> Base de données >  >> RDS >> Database

Remplacement des curseurs SQL par des alternatives pour éviter les problèmes de performances

Dans cet article, nous examinerons quelques alternatives à l'utilisation de curseurs SQL qui peuvent aider à éviter les problèmes de performances causés par l'utilisation de curseurs.

Avant de discuter des alternatives, passons en revue le concept général des curseurs SQL.

Présentation rapide des curseurs SQL

Les curseurs SQL sont principalement utilisés lorsque les opérations basées sur des ensembles ne sont pas applicables et que vous devez accéder aux données et effectuer des opérations une ligne à la fois plutôt que d'appliquer une seule opération basée sur des ensembles à un objet entier (tel qu'une table ou un ensemble de tableaux).

Définition simple

Un curseur SQL permet d'accéder aux données une ligne à la fois, vous donnant ainsi un contrôle direct ligne par ligne sur le jeu de résultats.

Définition Microsoft

Selon la documentation Microsoft, les instructions Microsoft SQL Server produisent un jeu de résultats complet, mais il est parfois préférable de le traiter une ligne à la fois, ce qui peut être fait en ouvrant un curseur sur le jeu de résultats.

Le processus en 5 étapes de l'utilisation d'un curseur

Le processus d'utilisation d'un curseur SQL peut être généralement décrit comme suit :

  1. Déclarer le curseur
  2. Ouvrir le curseur
  3. Récupérer les lignes
  4. Fermer le curseur
  5. Désallouer le curseur

Remarque importante

Veuillez garder à l'esprit que, selon Vaidehi Pandere, les curseurs sont des pointeurs qui occupent votre mémoire système - qui serait autrement réservée à d'autres processus importants. C'est pourquoi parcourir un grand ensemble de résultats à l'aide de curseurs n'est généralement pas la meilleure idée, à moins qu'il n'y ait une raison légitime de le faire.

Pour des informations plus détaillées à ce sujet, n'hésitez pas à vous référer à mon article Comment utiliser les curseurs SQL à des fins spéciales.

Exemple de curseur SQL

Tout d'abord, nous allons voir un exemple d'utilisation d'un curseur SQL pour renommer les objets d'une base de données un par un.

Afin de créer un curseur SQL dont nous avons besoin, configurons un exemple de base de données afin que nous puissions y exécuter nos scripts.

Configurer une base de données d'exemple (UniversityV3)

Exécutez le script suivant pour créer et remplir l'exemple de base de données UniversityV3 avec deux tables :

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

Créer un curseur SQL pour renommer les tables (_Backup)

Envisagez maintenant de répondre à la spécification suivante en utilisant un curseur :

  1. Nous devons ajouter "_Backup" aux noms de toutes les tables existantes dans une base de données
  2. Les tables qui ont déjà "_Backup" dans leur nom ne doivent pas être renommées

Créons un curseur SQL pour renommer toutes les tables de la base de données exemple en ajoutant "_Backup" au nom de chaque table tout en veillant également à ce que les tables contenant "_Backup" dans leur nom ne soient pas renommées à nouveau en exécutant le code suivant :

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

Exécuter le script de renommage et afficher les résultats

Maintenant, appuyez sur F5 dans SSMS (SQL Server Management Studio) pour exécuter le script et voir les résultats :

L'actualisation des noms des tables dans l'explorateur d'objets SSMS montre clairement que nous les avons modifiés avec succès comme spécifié.

Exécutons à nouveau le script en appuyant à nouveau sur F5 et regardons les résultats :

Création d'un curseur SQL pour réinitialiser _Backup Naming

Nous devons également créer un script qui utilise un curseur SQL pour rétablir les noms des tables que nous venons de modifier aux noms initiaux - nous le ferons en supprimant "_Backup" de leurs noms.

Le script ci-dessous nous permettra de faire exactement cela :

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

Exécuter le script de réinitialisation et afficher les résultats

L'exécution du script montre que les noms de table ont été réinitialisés avec succès :

Ce sont les exemples de certains scénarios dans lesquels il est difficile d'éviter d'utiliser des curseurs SQL en raison de la nature de l'exigence. Cependant, il est toujours possible de trouver une approche alternative.

Alternatives au curseur SQL

Il existe deux alternatives les plus courantes pour les curseurs SQL, alors examinons chacune d'elles en détail.

Alternative 1 :Variables de tableau

L'une de ces alternatives sont les variables de table.

Les variables de table, tout comme les tables, peuvent stocker plusieurs résultats, mais avec certaines limitations. Selon la documentation Microsoft, une variable de table est un type de données spécial utilisé pour stocker un ensemble de résultats pour un traitement ultérieur.

Cependant, gardez à l'esprit que les variables de table sont mieux utilisées avec de petits ensembles de données.

Les variables de table peuvent être très efficaces pour les requêtes à petite échelle, car elles fonctionnent comme des variables locales et sont automatiquement nettoyées lorsqu'elles sortent de la portée.

Stratégie des variables de tableau :

Nous utiliserons des variables de table au lieu de curseurs SQL pour renommer toutes les tables d'une base de données en suivant ces étapes :

  1. Déclarer une variable de table
  2. Stocker les noms et les identifiants des tables dans la variable de table que nous avons déclarée
  3. Définissez le compteur sur 1 et obtenez le nombre total d'enregistrements à partir de la variable de table
  4. Utiliser une boucle "while" tant que le compteur est inférieur ou égal au nombre total d'enregistrements
  5. Dans la boucle "while", nous renommerons les tables une par une tant qu'elles ne sont pas déjà renommées et augmenterons le compteur pour chaque table

Code variable du tableau :

Exécutez le script SQL suivant qui crée et utilise une variable de table pour renommer les tables :

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

Exécuter le script et afficher les résultats

Maintenant, exécutons le script et vérifions les résultats :

Alternative 2 :Tables temporaires

Nous pouvons également utiliser des tables temporaires au lieu de curseurs SQL pour itérer le jeu de résultats une ligne à la fois.

Les tables temporaires sont utilisées depuis longtemps et constituent un excellent moyen de remplacer les curseurs pour les grands ensembles de données.

Tout comme les variables de table, les tables temporaires peuvent contenir le jeu de résultats afin que nous puissions effectuer les opérations nécessaires en le traitant avec un algorithme d'itération tel qu'une boucle "while".

Stratégie de table temporaire :

Nous allons utiliser une table temporaire pour renommer toutes les tables de l'exemple de base de données en suivant ces étapes :

  1. Déclarer une table temporaire
  2. Stocker les noms et identifiants des tables dans la table temporaire que nous venons de déclarer
  3. Définissez le compteur sur 1 et obtenez le nombre total d'enregistrements de la table temporaire
  4. Utiliser une boucle "while" tant que le compteur est inférieur ou égal au nombre total d'enregistrements
  5. Dans la boucle "while", renommez les tables une par une tant qu'elles ne sont pas déjà renommées et augmentez le compteur pour chaque table

Réinitialiser les tableaux

Nous devons réinitialiser les noms des tables à leur forme initiale en supprimant '_Backup' à la fin de leurs noms, veuillez donc relancer le script de réinitialisation que nous avons déjà écrit et utilisé ci-dessus afin que nous puissions appliquer une autre méthode pour renommer les tables.

Code de table temporaire :

Exécutez le script SQL suivant pour créer et utiliser une table temporaire pour renommer toutes les tables de notre base de données :

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

Exécuter le script et vérifier la sortie

Maintenant, exécutons le script pour afficher les résultats :

Choses à faire

Maintenant que vous êtes familiarisé avec les alternatives aux curseurs SQL, telles que l'utilisation de variables de table et de tables temporaires, essayez de faire les choses suivantes pour vous familiariser avec l'application de ces connaissances dans la pratique :

  1. Créer et renommer les index de toutes les tables d'un exemple de base de données - d'abord via un curseur, puis en utilisant des méthodes alternatives (variables de table et tables temporaires)
  2. Rétablir les noms des tables de cet article à leurs noms initiaux en utilisant des méthodes alternatives (tables temporaires et variables de table)
  3. Vous pouvez également vous référer au(x) premier(s) exemple(s) de mon article Comment utiliser les curseurs SQL à des fins spéciales et essayer de remplir des tables avec beaucoup de lignes et mesurer les statistiques et le temps pour les requêtes afin de comparer la méthode de curseur de base avec les alternatives