Este artículo describe los cursores SQL y cómo usarlos para algunos propósitos especiales. Resalta la importancia de los cursores SQL junto con sus desventajas.
No siempre se usa el cursor SQL en la programación de bases de datos, pero su comprensión conceptual y aprender a usarlos ayuda mucho a comprender cómo realizar tareas excepcionales en la programación T-SQL.
Descripción general de los cursores SQL
Repasemos algunos conceptos básicos de los cursores SQL si no está familiarizado con ellos.
Definición sencilla
Un cursor SQL brinda acceso a los datos una fila a la vez, lo que le brinda más control (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 momentos en que los resultados se procesan mejor una fila a la vez. Abrir un cursor en un conjunto de resultados permite procesar el conjunto de resultados una fila a la vez.
T-SQL y conjunto de resultados
Dado que tanto las definiciones simples como las de Microsoft del cursor SQL mencionan un conjunto de resultados, intentemos comprender qué es exactamente el conjunto de resultados en el contexto de la programación de bases de datos. Vamos a crear y llenar rápidamente la tabla de Estudiantes en una base de datos de muestra UniversityV3 de la siguiente manera:
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) ); -- (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
Ahora, seleccione todas las filas del Estudiante tabla:
-- View Student table data SELECT [StudentId], [Name], [Course], [Marks], [ExamDate] FROM dbo.Student
Este es el conjunto de resultados que se devuelve como resultado de seleccionar todos los registros del Estudiante mesa.
T-SQL y teoría de conjuntos
T-SQL se basa puramente en los siguientes dos conceptos matemáticos:
- Teoría de conjuntos
- Lógica de predicados
La teoría de conjuntos, como su nombre lo indica, es una rama de las matemáticas sobre conjuntos que también pueden llamarse colecciones de objetos distintos definidos.
En resumen, en la teoría de conjuntos, pensamos en cosas u objetos como un todo de la misma manera que pensamos en un elemento individual.
Por ejemplo, el estudiante es un conjunto de todos los estudiantes distintos definidos, por lo que tomamos un estudiante como un todo, lo cual es suficiente para obtener detalles de todos los estudiantes en ese conjunto (tabla).
Consulte mi artículo The Art of Aggregating Data in SQL from Simple to Sliding Aggregations para obtener más detalles.
Cursores y operaciones basadas en filas
T-SQL está diseñado principalmente para realizar operaciones basadas en conjuntos, como seleccionar todos los registros de una tabla o eliminar todas las filas de una tabla.
En resumen, T-SQL está especialmente diseñado para trabajar con tablas en forma de conjuntos, lo que significa que pensamos en una tabla como un todo, y cualquier operación como seleccionar, actualizar o eliminar se aplica como un todo a la tabla o a ciertos filas que cumplen los criterios.
Sin embargo, hay casos en los que es necesario acceder a las tablas fila por fila en lugar de como un solo conjunto de resultados, y aquí es cuando los cursores entran en acción.
Según Vaidehi Pandere, a veces la lógica de la aplicación necesita trabajar con una fila a la vez en lugar de todas las filas a la vez, lo que es lo mismo que hacer un bucle (uso de bucles para iterar) a través de todo el conjunto de resultados.
Conceptos básicos de los cursores SQL con ejemplos
Hablemos ahora más sobre los cursores SQL.
En primer lugar, aprendamos o revisemos (aquellos que ya están familiarizados con el uso de cursores en T-SQL) cómo usar el cursor en T-SQL.
El uso del cursor SQL es un proceso de cinco pasos expresado de la siguiente manera:
- Declarar Cursor
- Cursor abierto
- Obtener filas
- Cerrar cursor
- Desasignar cursor
Paso 1:Declarar Cursor
El primer paso es declarar el cursor SQL para que pueda usarse después.
El cursor SQL se puede declarar de la siguiente manera:
DECLARE Cursor <Cursor_Name> for <SQL statement>
Paso 2:Abrir cursor
El siguiente paso después de la declaración es abrir el cursor, lo que significa llenar el cursor con el conjunto de resultados que se expresa de la siguiente manera:
Open <Cursor_Name>
Paso 3:obtener filas
Una vez que se declara y abre el cursor, el siguiente paso es comenzar a recuperar las filas del cursor SQL una por una para que busque las filas y obtenga la siguiente fila disponible del cursor SQL:
Fetch Next from <Cursor_Name>
Paso 4:Cerrar el cursor
Una vez que las filas se recuperan una por una y se manipulan según los requisitos, el siguiente paso es cerrar el cursor SQL.
Cerrar el cursor SQL realiza tres tareas:
- Libera el conjunto de resultados que tiene actualmente el cursor
- Libera cualquier bloqueo de cursor en las filas por el cursor
- Cierra el cursor abierto
La sintaxis simple para cerrar el cursor es la siguiente:
Close <Cursor_Name>
Paso 5:desasignar cursor
El último paso en este sentido es desasignar el cursor, lo que elimina la referencia del cursor.
La sintaxis es la siguiente:
DEALLOCATE <Cursor_Name>
Compatibilidad de cursores SQL
Según la documentación de Microsoft, los cursores SQL son compatibles con las siguientes versiones:
- SQL Server 2008 y versiones posteriores
- Base de datos Azure SQL
Cursor SQL Ejemplo 1:
Ahora que estamos familiarizados con los pasos necesarios para implementar el cursor SQL, veamos un ejemplo simple del uso del cursor SQL:
-- Declare Student cursor example 1 USE UniversityV3 GO DECLARE Student_Cursor CURSOR FOR SELECT StudentId ,[Name] FROM dbo.Student; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor WHILE @@FETCH_STATUS = 0 BEGIN FETCH NEXT FROM Student_Cursor END CLOSE Student_Cursor DEALLOCATE Student_Cursor
La salida es la siguiente:
Cursor SQL Ejemplo 2:
En este ejemplo, vamos a usar dos variables para almacenar los datos retenidos por el cursor a medida que se mueve de una fila a otra para que podamos mostrar el conjunto de resultados una fila a la vez mostrando los valores de las variables.
-- Declare Student cursor with variables example 2 USE UniversityV3 GO DECLARE @StudentId INT ,@StudentName VARCHAR(40) -- Declare variables to hold row data held by cursor DECLARE Student_Cursor CURSOR FOR SELECT StudentId ,[Name] FROM dbo.Student; OPEN Student_Cursor FETCH NEXT FROM Student_Cursor INTO @StudentId, @StudentName -- Fetch first row and store it into variables WHILE @@FETCH_STATUS = 0 BEGIN PRINT CONCAT(@StudentId,'--', @StudentName) -- Show variables data FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it into variables INTO @StudentId, @StudentName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
El resultado del código SQL anterior es el siguiente:
Se podría argumentar que podemos lograr el mismo resultado usando un script SQL simple de la siguiente manera:
-- Viewing student id and name without SQL cursor SELECT StudentId,Name FROM dbo.Student order by StudentId
De hecho, hay bastantes tareas que requieren el uso de cursores SQL a pesar de que se desaconseja usar cursores SQL debido a su impacto directo en la memoria.
Nota importante
Tenga en cuenta que, según Vaidehi Pandere, los cursores son un conjunto de punteros residentes en la memoria, por lo que ocupan la memoria del sistema que, de lo contrario, sería utilizada por otros procesos importantes; es por eso que recorrer un gran conjunto de resultados a través de cursores nunca es una buena idea a menos que haya una razón legítima para ello.
Uso de cursores SQL para propósitos especiales
Veremos algunos propósitos especiales para los que se pueden usar los cursores SQL.
Pruebas de memoria del servidor de base de datos
Dado que los cursores SQL tienen un gran impacto en la memoria del sistema, son buenos candidatos para replicar escenarios en los que es necesario investigar el uso excesivo de memoria por parte de diferentes procedimientos almacenados o secuencias de comandos SQL ad-hoc.
Una forma sencilla de entender esto es hacer clic en el botón de estadísticas del cliente en la barra de herramientas (o presionar Shift+Alt+S) en SSMS (SQL Server Management Studio) y ejecutar una consulta simple sin cursor:
Ahora ejecute la consulta con el cursor usando variables (SQL Cursor Example 2):
Ahora anote las diferencias:
Número de sentencias SELECT sin cursor:1
Número de declaraciones SELECT con cursor:7
Número de viajes de ida y vuelta al servidor sin cursor:1
Número de viajes de ida y vuelta al servidor con el cursor:2
Tiempo de procesamiento del cliente sin cursor:1
Tiempo de procesamiento del cliente con el cursor:8
Tiempo total de ejecución sin cursor:1
Tiempo total de ejecución con cursor:38
Tiempo de espera en las respuestas del servidor sin cursor:0
Tiempo de espera en las respuestas del servidor con el cursor:30
En resumen, ejecutar la consulta sin el cursor que devuelve solo 5 filas es ejecutar la misma consulta 6 o 7 veces con el cursor.
Ahora puede imaginar lo fácil que es replicar el impacto de la memoria usando cursores, sin embargo, esto no siempre es lo mejor que se puede hacer.
Tareas de manipulación masiva de objetos de base de datos
Hay otra área donde los cursores SQL pueden ser útiles y es cuando tenemos que realizar una operación masiva en bases de datos u objetos de bases de datos.
Para entender esto, primero, necesitamos crear la tabla del curso y completarla en UniversityV3 base de datos de la siguiente manera:
-- Create Course table 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) ); -- 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
Ahora supongamos que queremos cambiar el nombre de todas las tablas existentes en UniversityV3 base de datos como tablas ANTIGUAS.
Esto requiere la iteración del cursor sobre todas las tablas una por una para que se les pueda cambiar el nombre.
El siguiente código hace el trabajo:
-- Declare Student cursor to rename all the tables as old 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 SET @[email protected]+'_OLD' -- Add _OLD to exsiting name of the table EXEC sp_rename @TableName,@NewTableName -- Rename table as OLD table FETCH NEXT FROM Student_Cursor -- Get next row data into cursor and store it into variables INTO @TableName END CLOSE Student_Cursor -- Close cursor locks on the rows DEALLOCATE Student_Cursor -- Release cursor reference
Enhorabuena, ha cambiado correctamente el nombre de todas las tablas existentes con el cursor SQL.
Cosas que hacer
Ahora que está familiarizado con el uso del cursor SQL, intente lo siguiente:
- Intente crear y cambiar el nombre de los índices de todas las tablas de una base de datos de ejemplo con el cursor.
- Intente revertir las tablas renombradas en este artículo a los nombres originales usando el cursor.
- Intente completar las tablas con muchas filas y mida las estadísticas y el tiempo de las consultas con y sin el cursor.