Observação:esta postagem foi publicada originalmente apenas em nosso eBook, Técnicas de alto desempenho para SQL Server, Volume 3. Você pode descobrir mais sobre nossos eBooks aqui.
Há mais de três anos, escrevi um post sobre opções de cursor no SQL Server e por que você deve substituir os padrões:
- Qual o impacto que diferentes opções de cursor podem ter?
Eu queria postar um acompanhamento para reiterar que – embora você nunca deva apenas aceitar os padrões – você realmente deve pensar sobre quais opções são mais aplicáveis ao seu cenário. Eu também queria esclarecer alguns itens que surgiram nos comentários desse post.
Andrew Kelly trouxe um grande ponto, e é que um
STATIC
cursor faz uma cópia única dos resultados, coloca-os em tempdb e evita quaisquer problemas de simultaneidade que possam afetar um DYNAMIC
cursor. Uma opção não é uma vencedora clara sobre a outra em todos os casos; por exemplo, você pode ter muitos cursores (ou cursores com conjuntos de resultados muito grandes) e/ou um tempdb já sobrecarregado e não deseja descarregar nenhum estresse adicional lá. Mas é algo digno de teste. Fabiano também trouxe um grande ponto que tanto
DYNAMIC
e FAST_FORWARD
cursores podem ser vulnerável ao problema do Halloween (discutido por Paul White em uma série de 4 partes, começando aqui). Paul também comentou que FAST_FORWARD
pode não ser vulnerável ao problema, dependendo se o otimizador escolheu um plano estático ou dinâmico (Marc Friedman da Microsoft entra em grandes detalhes sobre isso aqui). Por fim, gostaria de salientar que nem todos os cursores padrão são criados iguais. Executei alguns testes e verifiquei como o SQL Server decidiu definir opções de cursor em vários cenários (validados usando o
sys.dm_exec_cursors
função de gestão dinâmica). O código é bem simples:DECLARE c CURSOR FOR [...blah blah...]; SELECT properties FROM sys.dm_exec_cursors(@@SPID);
Aqui estão os resultados para os cenários que testei:
A consulta do cursor é baseada em… | Tipo | Simultaneidade | Escopo |
---|---|---|---|
uma constante (FOR SELECT 1 ou FOR SELECT SYSDATETIME() ) | Instantâneo | Somente leitura | Global |
uma tabela #temp / ##temp | Dinâmico | Otimista | Global |
uma tabela/visualização de usuário | Dinâmico | Otimista | Global |
uma visualização de catálogo / DMV | Instantâneo | Somente leitura | Global |
uma junção #tmp -> tabela/visualização do usuário | Dinâmico | Otimista | Global |
uma junção #tmp -> visualização de catálogo / DMV | Instantâneo | Somente leitura | Global |
uma tabela / visualização de usuário de junção -> visualização de catálogo / DMV | Instantâneo | Somente leitura | Global |
Crédito onde o crédito é devido – esta investigação foi desencadeada por uma resposta de Jeroen Mostert no Stack Overflow.
Portanto, você deve estar ciente de que as opções padrão do seu cursor, se você não as substituir, podem ser diferentes dependendo da consulta subjacente ao cursor. Se você espera um comportamento específico em um ou em todos os casos, adquira o hábito de especificar explicitamente as opções desejadas.
Mas, na verdade, a questão é…
…pare de usar cursores. Existem realmente muito poucos problemas hoje em que a melhor solução é um cursor, especialmente se você estiver no SQL Server 2012 ou melhor – onde praticamente todos os problemas tradicionalmente resolvidos por cursores podem ser resolvidos usando aprimoramentos nas funções de janela. Se você ainda sentir que precisa usar cursores, siga os conselhos deste post e seu antecessor para determinar quais opções você deve usar.