Este artigo descreve os cursores SQL e como usá-los para algumas finalidades especiais. Ele destaca a importância dos cursores SQL junto com suas desvantagens.
Nem sempre é o caso de você usar o cursor SQL na programação de banco de dados, mas sua compreensão conceitual e aprender como usá-los ajuda muito a entender como executar tarefas excepcionais na programação T-SQL.
Visão geral dos cursores SQL
Vamos passar por alguns conceitos básicos de cursores SQL se você não estiver familiarizado com eles.
Definição simples
Um cursor SQL fornece acesso aos dados uma linha de cada vez, dando a você mais controle (linha por linha) sobre o conjunto de resultados.
Definição da Microsoft
De acordo com a documentação da Microsoft, as instruções do Microsoft SQL Server produzem um conjunto de resultados completo, mas há momentos em que os resultados são melhor processados uma linha por vez. Abrir um cursor em um conjunto de resultados permite processar o conjunto de resultados uma linha por vez.
T-SQL e conjunto de resultados
Como as definições simples e da Microsoft do cursor SQL mencionam um conjunto de resultados, vamos tentar entender o que exatamente é o conjunto de resultados no contexto da programação de banco de dados. Vamos criar e preencher rapidamente a tabela Estudantes em um banco de dados de exemplo UniversityV3 da seguinte forma:
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
Agora, selecione todas as linhas do campo Aluno tabela:
-- View Student table data SELECT [StudentId], [Name], [Course], [Marks], [ExamDate] FROM dbo.Student
Este é o conjunto de resultados que é retornado como resultado da seleção de todos os registros do Aluno tabela.
T-SQL e Teoria dos Conjuntos
O T-SQL é puramente baseado nos dois conceitos matemáticos a seguir:
- Teoria dos Conjuntos
- Lógica de predicados
A teoria dos conjuntos, como o nome indica, é um ramo da matemática sobre conjuntos que também pode ser chamado de coleções de objetos distintos definidos.
Em suma, na teoria dos conjuntos, pensamos em coisas ou objetos como um todo da mesma forma que pensamos em um item individual.
Por exemplo, o aluno é um conjunto de todos os alunos distintos definidos, então, tomamos um aluno como um todo o que é suficiente para obter detalhes de todos os alunos desse conjunto (tabela).
Consulte meu artigo A arte de agregar dados em SQL de agregações simples a deslizantes para obter mais detalhes.
Cursores e operações baseadas em linhas
O T-SQL foi projetado principalmente para executar operações baseadas em conjunto, como selecionar todos os registros de uma tabela ou excluir todas as linhas de uma tabela.
Resumindo, o T-SQL é especialmente projetado para trabalhar com tabelas de forma baseada em conjuntos, o que significa que pensamos em uma tabela como um todo, e qualquer operação como selecionar, atualizar ou excluir é aplicada como um todo à tabela ou a determinadas linhas que satisfaçam os critérios.
No entanto, há casos em que as tabelas precisam ser acessadas linha por linha em vez de um único conjunto de resultados, e é nesse momento que os cursores entram em ação.
De acordo com Vaidehi Pandere, às vezes a lógica do aplicativo precisa trabalhar com uma linha de cada vez, em vez de todas as linhas de uma vez, o que é o mesmo que fazer um loop (uso de loops para iterar) por todo o conjunto de resultados.
Noções básicas de cursores SQL com exemplos
Vamos agora discutir mais sobre cursores SQL.
Antes de tudo, vamos aprender ou revisar (aqueles que já estão familiarizados com o uso de cursores em T-SQL) como usar cursor em T-SQL.
O uso do cursor SQL é um processo de cinco etapas expresso da seguinte forma:
- Declarar cursor
- Abrir cursor
- Buscar linhas
- Fechar cursor
- Desalocar cursor
Etapa 1:declarar o cursor
O primeiro passo é declarar o cursor SQL para que possa ser usado posteriormente.
O cursor SQL pode ser declarado da seguinte forma:
DECLARE Cursor <Cursor_Name> for <SQL statement>
Etapa 2:abrir o cursor
O próximo passo após a declaração é abrir o cursor, o que significa preencher o cursor com o conjunto de resultados que é expresso da seguinte forma:
Open <Cursor_Name>
Etapa 3:buscar linhas
Uma vez que o cursor é declarado e aberto, o próximo passo é começar a recuperar as linhas do cursor SQL uma a uma para que as linhas de busca obtenham a próxima linha disponível do cursor SQL:
Fetch Next from <Cursor_Name>
Etapa 4:Fechar o cursor
Uma vez que as linhas são buscadas uma a uma e manipuladas conforme o requisito, a próxima etapa é fechar o cursor SQL.
Fechar o cursor SQL executa três tarefas:
- Libera o conjunto de resultados atualmente mantido pelo cursor
- Libera quaisquer bloqueios de cursor nas linhas pelo cursor
- Fecha o cursor aberto
A sintaxe simples para fechar o cursor é a seguinte:
Close <Cursor_Name>
Etapa 5:desalocar cursor
A etapa final a esse respeito é desalocar o cursor que remove a referência do cursor.
A sintaxe é a seguinte:
DEALLOCATE <Cursor_Name>
Compatibilidade do cursor SQL
De acordo com a documentação da Microsoft, os cursores SQL são compatíveis com as seguintes versões:
- SQL Server 2008 e versões posteriores
- Banco de dados SQL do Azure
Exemplo de cursor SQL 1:
Agora que estamos familiarizados com as etapas envolvidas para implementar o cursor SQL, vejamos um exemplo simples de uso do 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
A saída é a seguinte:
Exemplo de cursor SQL 2:
Neste exemplo, vamos usar duas variáveis para armazenar os dados mantidos pelo cursor conforme ele se move de linha em linha, para que possamos exibir o conjunto de resultados uma linha por vez exibindo os valores das variáveis.
-- 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
O resultado do código SQL acima é o seguinte:
Alguém poderia argumentar que podemos obter a mesma saída usando um script SQL simples da seguinte maneira:
-- Viewing student id and name without SQL cursor SELECT StudentId,Name FROM dbo.Student order by StudentId
Na verdade, existem algumas tarefas que exigem o uso de cursores SQL, apesar de ser desencorajado o uso de cursores SQL devido ao impacto direto na memória.
Observação importante
Por favor, tenha em mente que, de acordo com Vaidehi Pandere, os cursores são um conjunto de ponteiros residentes na memória, de modo que ocupam a memória do sistema que, de outra forma, seria usada por outros processos importantes; é por isso que percorrer um grande conjunto de resultados por meio de cursores nunca é uma boa ideia, a menos que haja uma razão legítima para isso.
Uso de cursores SQL para fins especiais
Passaremos por alguns propósitos especiais para os quais os cursores SQL podem ser usados.
Teste de memória do servidor de banco de dados
Como os cursores SQL têm um alto impacto na memória do sistema, eles são bons candidatos para replicar cenários em que o uso excessivo de memória por diferentes procedimentos armazenados ou scripts SQL ad-hoc precisa ser investigado.
Uma maneira simples de entender isso é clicar no botão de estatísticas do cliente na barra de ferramentas (ou pressionar Shift+Alt+S) no SSMS (SQL Server Management Studio) e executar uma consulta simples sem cursor:
Agora execute a consulta com o cursor usando variáveis (SQL Cursor Exemplo 2):
Agora, anote as diferenças:
Número de instruções SELECT sem cursor:1
Número de instruções SELECT com cursor:7
Número de viagens de ida e volta do servidor sem cursor:1
Número de viagens de ida e volta do servidor com cursor:2
Tempo de processamento do cliente sem cursor:1
Tempo de processamento do cliente com cursor:8
Tempo total de execução sem cursor:1
Tempo total de execução com cursor:38
Tempo de espera nas respostas do servidor sem cursor:0
Tempo de espera nas respostas do servidor com cursor:30
Resumindo, executar a consulta sem o cursor que retorna apenas 5 linhas é executar a mesma consulta 6-7 vezes com o cursor.
Agora você pode imaginar como é fácil replicar o impacto na memória usando cursores, no entanto, isso nem sempre é a melhor coisa a fazer.
Tarefas de manipulação de objetos de banco de dados em massa
Há outra área onde os cursores SQL podem ser úteis e é quando temos que realizar uma operação em massa em bancos de dados ou objetos de banco de dados.
Para entender isso, primeiro, precisamos criar a tabela Course e preenchê-la no UniversityV3 banco de dados da seguinte forma:
-- 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
Agora suponha que queremos renomear todas as tabelas existentes no UniversityV3 banco de dados como tabelas OLD.
Isso requer a iteração do cursor sobre todas as tabelas, uma por uma, para que elas possam ser renomeadas.
O código a seguir faz o trabalho:
-- 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
Parabéns, você renomeou com sucesso todas as tabelas existentes usando o cursor SQL.
Coisas para fazer
Agora que você está familiarizado com o uso do cursor SQL, tente as seguintes coisas:
- Tente criar e renomear índices de todas as tabelas de um banco de dados de exemplo através do cursor.
- Tente reverter as tabelas renomeadas neste artigo para os nomes originais usando o cursor.
- Tente preencher tabelas com muitas linhas e medir as estatísticas e o tempo das consultas com e sem o cursor.