En este artículo, veremos algunas alternativas al uso de cursores SQL que pueden ayudar a evitar problemas de rendimiento causados por el uso de cursores.
Antes de discutir las alternativas, repasemos el concepto general de los cursores SQL.
Descripción general rápida de los cursores SQL
Los cursores de SQL se utilizan principalmente cuando las operaciones basadas en conjuntos no son aplicables y se requiere acceder a los datos y realizar operaciones fila por fila en lugar de aplicar una sola operación basada en conjuntos a un objeto completo (como una tabla o un conjunto de tablas).
Definición sencilla
Un cursor SQL brinda acceso a los datos de una fila a la vez, lo que le brinda un control directo fila por fila sobre el conjunto de resultados.
Definición de Microsoft
De acuerdo con la documentación de Microsoft, las declaraciones de Microsoft SQL Server producen un conjunto de resultados completo, pero hay ocasiones en las que es mejor procesarlas una fila a la vez, lo que se puede hacer abriendo un cursor en el conjunto de resultados.
El proceso de 5 pasos para usar un cursor
El proceso de usar un cursor SQL se puede describir generalmente de la siguiente manera:
- Declarar Cursor
- Cursor abierto
- Obtener filas
- Cerrar cursor
- Desasignar cursor
Nota importante
Tenga en cuenta que, según Vaidehi Pandere, los cursores son punteros que ocupan la memoria del sistema, que de otro modo estaría reservada para otros procesos importantes. Es por eso que recorrer un gran conjunto de resultados usando cursores no suele ser la mejor idea, a menos que haya una razón legítima para hacerlo.
Para obtener información más detallada sobre esto, no dude en consultar mi artículo Cómo utilizar cursores SQL para fines especiales.
Ejemplo de cursor SQL
Primero, veremos un ejemplo de cómo se puede usar un cursor SQL para cambiar el nombre de los objetos de la base de datos uno por uno.
Para crear un cursor SQL que necesitamos, configuremos una base de datos de muestra para que podamos ejecutar nuestros scripts contra ella.
Configurar base de datos de muestra (UniversityV3)
Ejecute el siguiente script para crear y completar la base de datos de muestra UniversityV3 con dos tablas:
-- (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
Crear un cursor SQL para renombrar tablas (_Backup)
Ahora considere cumplir con la siguiente especificación usando un cursor:
- Necesitamos agregar '_Backup' a los nombres de todas las tablas existentes en una base de datos
- Las tablas que ya tienen '_Backup' en su nombre no deben cambiarse de nombre
Vamos a crear un cursor SQL para cambiar el nombre de todas las tablas en la base de datos de muestra agregando '_Backup' al nombre de cada tabla y asegurándonos de que las tablas que contienen '_Backup' en su nombre no se renombrarán nuevamente ejecutando el siguiente código:
-- 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
Ejecute el script de cambio de nombre y vea los resultados
Ahora, presione F5 en SSMS (SQL Server Management Studio) para ejecutar el script y ver los resultados:
Actualizar los nombres de las tablas en el explorador de objetos de SSMS muestra claramente que los hemos cambiado con éxito según lo especificado.
Ejecutemos el script nuevamente presionando F5 nuevamente y veamos los resultados:
Creación de un cursor SQL para restablecer _Backup Naming
También necesitamos crear una secuencia de comandos que use un cursor SQL para revertir los nombres de las tablas que acabamos de cambiar a los iniciales; lo haremos eliminando '_Backup' de sus nombres.
El siguiente script nos permitirá hacer precisamente eso:
-- 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
Ejecute el script de restablecimiento y vea los resultados
La ejecución del script muestra que los nombres de las tablas se han restablecido correctamente:
Estos fueron los ejemplos de algunos escenarios en los que es difícil evitar el uso de cursores SQL debido a la naturaleza del requisito. Sin embargo, todavía es posible encontrar un enfoque alternativo.
Alternativas de cursores SQL
Hay dos alternativas más comunes para los cursores SQL, así que veamos cada una de ellas en detalle.
Alternativa 1:Tabla de Variables
Una de estas alternativas son las variables de tabla.
Las variables de tabla, al igual que las tablas, pueden almacenar múltiples resultados, pero con algunas limitaciones. Según la documentación de Microsoft, una variable de tabla es un tipo de datos especial que se utiliza para almacenar un conjunto de resultados para su procesamiento posterior.
Sin embargo, tenga en cuenta que las variables de tabla se utilizan mejor con conjuntos de datos pequeños.
Las variables de tabla pueden ser muy eficientes para consultas a pequeña escala, ya que funcionan como variables locales y se limpian automáticamente al salir del alcance.
Estrategia de variables de tabla:
Usaremos variables de tabla en lugar de cursores SQL para cambiar el nombre de todas las tablas de una base de datos siguiendo estos pasos:
- Declarar una variable de tabla
- Almacene los nombres e ID de las tablas en la variable de tabla que declaramos
- Establezca el contador en 1 y obtenga el número total de registros de la variable de la tabla
- Utilice un bucle 'while' siempre que el contador sea menor o igual que el número total de registros
- Dentro del bucle 'while', cambiaremos el nombre de las tablas una por una siempre que no hayan cambiado de nombre y aumentemos el contador para cada tabla
Código de variable de tabla:
Ejecute el siguiente script SQL que crea y usa una variable de tabla para cambiar el nombre de las tablas:
-- 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
Ejecutar el script y ver los resultados
Ahora, ejecutemos el script y verifiquemos los resultados:
Alternativa 2:Tablas Temporales
También podemos usar tablas temporales en lugar de cursores SQL para iterar el conjunto de resultados una fila a la vez.
Las tablas temporales se han utilizado durante mucho tiempo y proporcionan una forma excelente de reemplazar los cursores para grandes conjuntos de datos.
Al igual que las variables de tabla, las tablas temporales pueden contener el conjunto de resultados para que podamos realizar las operaciones necesarias al procesarlo con un algoritmo de iteración como un bucle 'while'.
Estrategia de mesa temporal:
Usaremos una tabla temporal para cambiar el nombre de todas las tablas en la base de datos de muestra siguiendo estos pasos:
- Declarar una tabla temporal
- Almacene los nombres de las tablas y las identificaciones en la tabla temporal que acabamos de declarar
- Establezca el contador en 1 y obtenga el número total de registros de la tabla temporal
- Utilice un bucle 'while' siempre que el contador sea menor o igual que el número total de registros
- Dentro del bucle 'while', cambie el nombre de las tablas una por una siempre que no hayan cambiado de nombre y aumente el contador para cada tabla
Restablecer las tablas
Necesitamos restablecer los nombres de las tablas a su forma inicial eliminando '_Backup' del final de sus nombres, así que vuelva a ejecutar el script de restablecimiento que ya escribimos y usamos anteriormente para que podamos aplicar otro método para cambiar el nombre de las tablas.
Código de tabla temporal:
Ejecute el siguiente script SQL para crear y usar una tabla temporal para cambiar el nombre de todas las tablas en nuestra base de datos:
-- 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
Ejecute el script y compruebe el resultado
Ahora, ejecutemos el script para ver los resultados:
Cosas que hacer
Ahora que está familiarizado con las alternativas a los cursores SQL, como el uso de variables de tabla y tablas temporales, intente hacer lo siguiente para familiarizarse con la aplicación de este conocimiento en la práctica:
- Cree y cambie el nombre de los índices de todas las tablas en una base de datos de muestra, primero a través de un cursor y luego usando métodos alternativos (variables de tabla y tablas temporales)
- Revertir los nombres de las tablas de este artículo a sus nombres iniciales utilizando métodos alternativos (tablas temporales y variables de tabla)
- También puede consultar los primeros ejemplos en mi artículo Cómo usar cursores SQL para propósitos especiales e intentar completar tablas con muchas filas y medir las estadísticas y el tiempo de las consultas para comparar el método básico del cursor con las alternativas.