Pergunta

É geralmente aceito que o uso de cursores em procedimentos armazenados deve ser evitado sempre que possível (substituído por lógica baseada em conjunto, etc.).Se você considerar os casos em que precisa iterar alguns dados e pode fazê-lo de maneira somente leitura, o cursor de avanço rápido (avanço somente leitura) é mais ou menos ineficiente do que, digamos, loops while?Pelas minhas investigações, parece que a opção do cursor é geralmente mais rápida e usa menos leituras e tempo de CPU.Não fiz nenhum teste extenso, mas é isso que os outros encontram?Os cursores desse tipo (avanço rápido) carregam sobrecarga ou recursos adicionais que podem ser caros e que eu não conheço.

Toda a conversa sobre não usar cursores é realmente sobre evitar o uso de cursores quando abordagens baseadas em conjuntos estão disponíveis e o uso de cursores atualizáveis, etc.

Obrigado

Foi útil?

Solução

A 'Prática recomendada' para evitar cursores no SQL Server remonta ao SQL Server 2000 e versões anteriores.A reescrita do mecanismo no SQL 2005 abordou a maioria dos problemas relacionados aos problemas dos cursores, principalmente com a introdução da opção de avanço rápido.Os cursores não são necessariamente piores do que os baseados em conjuntos e são usados ​​extensivamente e com sucesso no Oracle PL/SQL (LOOP).

O 'geralmente aceito' a que você se refere era válido, mas agora está desatualizado e incorreto - suponha que os cursores de avanço rápido se comportam conforme anunciado e funcionam.Faça alguns testes e pesquisas, baseando suas descobertas no SQL2005 e posteriores

Outras dicas

Embora um cursor de avanço rápido tenha algumas otimizações no Sql Server 2005, é não é verdade que eles estão próximos de uma consulta baseada em conjunto em termos de desempenho.Existem muito poucas situações em que a lógica do cursor não pode ser substituída por uma consulta baseada em conjunto.Os cursores sempre serão inerentemente mais lentos, em parte devido ao fato de que você precisa interromper continuamente a execução para preencher suas variáveis ​​locais.

Aqui estão algumas referências, que seriam apenas a ponta do iceberg se você pesquisar esse assunto:

http://www.code-magazine.com/Article.aspx?quickid=060113

http://dataeducation.com/re-inventing-the-recursive-cte/

Esta resposta pretende consolidar as respostas dadas até à data.

1) Se possível, use lógica baseada em conjunto para suas consultas, ou seja,tente usar apenas SELECT, INSERT, UPDATE ou DELETE com o apropriado FROM cláusulas ou consultas aninhadas - quase sempre serão mais rápidas.

2) Se o acima não for possível, então no SQL Server 2005+ FAST FORWARD os cursores são eficientes e têm bom desempenho e devem ser usados ​​preferencialmente aos loops while.

"Se você quiser um cursor ainda mais rápido que o FAST FORWARD, use um cursor ESTÁTICO.Eles são mais rápidos que o FAST FORWARD.Não é extremamente rápido, mas pode fazer a diferença."

Não tão rápido!De acordo com a Microsoft:"Normalmente, quando essas conversões ocorriam, o tipo de cursor era degradado para um tipo de cursor 'mais caro'.Geralmente, um cursor (FAST) FORWARD-ONLY tem o melhor desempenho, seguido por DYNAMIC, KEYSET e, finalmente, STATIC, que geralmente tem o menor desempenho.

de: http://blogs.msdn.com/b/mssqlisv/archive/2006/06/23/644493.aspx

Você pode evitar cursores na maioria das vezes, mas às vezes é necessário.

Apenas tenha em mente que FAST_FORWARD é DINÂMICO...FORWARD_ONLY você pode usar com um cursor STATIC.

Experimente usá-lo no problema do Halloween para ver o que acontece!!!

IF OBJECT_ID('Funcionarios') IS NOT NULL
DROP TABLE Funcionarios
GO

CREATE TABLE Funcionarios(ID          Int IDENTITY(1,1) PRIMARY KEY,
                          ContactName Char(7000),
                          Salario     Numeric(18,2));
GO

INSERT INTO Funcionarios(ContactName, Salario) VALUES('Fabiano', 1900)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Luciano',2050)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Gilberto', 2070)
INSERT INTO Funcionarios(ContactName, Salario) VALUES('Ivan', 2090)
GO

CREATE NONCLUSTERED INDEX ix_Salario ON Funcionarios(Salario)
GO

-- Halloween problem, will update all rows until then reach 3000 !!!
UPDATE Funcionarios SET Salario = Salario * 1.1
  FROM Funcionarios WITH(index=ix_Salario)
 WHERE Salario < 3000
GO

-- Simulate here with all different CURSOR declarations
-- DYNAMIC update the rows until all of then reach 3000
-- FAST_FORWARD update the rows until all of then reach 3000
-- STATIC update the rows only one time. 

BEGIN TRAN
DECLARE @ID INT
DECLARE TMP_Cursor CURSOR DYNAMIC 
--DECLARE TMP_Cursor CURSOR FAST_FORWARD
--DECLARE TMP_Cursor CURSOR STATIC READ_ONLY FORWARD_ONLY
    FOR SELECT ID 
          FROM Funcionarios WITH(index=ix_Salario)
         WHERE Salario < 3000

OPEN TMP_Cursor

FETCH NEXT FROM TMP_Cursor INTO @ID

WHILE @@FETCH_STATUS = 0
BEGIN
  SELECT * FROM Funcionarios WITH(index=ix_Salario)

  UPDATE Funcionarios SET Salario = Salario * 1.1 
   WHERE ID = @ID

  FETCH NEXT FROM TMP_Cursor INTO @ID
END

CLOSE TMP_Cursor
DEALLOCATE TMP_Cursor

SELECT * FROM Funcionarios

ROLLBACK TRAN
GO

As pessoas evitam o cursor porque geralmente são mais difíceis de escrever do que simples loops while; no entanto, um loop while pode ser caro porque você seleciona constantemente dados de uma tabela, temporários ou não.

Com um cursor, que é somente leitura, os dados são mantidos na memória e foram projetados especificamente para loop.

Este artigo destaca que um cursor médio é executado 50 vezes mais rápido que um loop while.

Algumas alternativas ao uso do cursor:

Enquanto as tabelas de tabelas de tábuas temperadas temp de loops associaram as declarações de casos de subconsivos múltiplos interrogatórios com frequência, as operações de cursor também podem ser alcançadas com técnicas não cursoras.

Se você tiver certeza de que o cursor precisa ser usado, o número de registros a serem processados ​​deverá ser reduzido tanto quanto possível.Uma maneira de fazer isso é fazer com que os registros sejam processados ​​primeiro em uma tabela temporária, não na tabela original, mas em um cursor que usará os registros na tabela temporária.Quando esse caminho é usado, presume-se que o número de registros na tabela temporária foi bastante reduzido em comparação com a tabela original.Com menos registros, o cursor é concluído mais rapidamente.

Algumas propriedades do cursor que afetam o desempenho incluem:

AVANÇAR_SOMENTE:Suporta o encaminhamento apenas do cursor da primeira linha até o final com FETCH NEXT.A menos que seja definida como KEYSET ou STATIC, a cláusula SELECT é reavaliada quando cada busca é chamada.

ESTÁTICO:Cria uma cópia temporária dos dados criados e é usada pelo cursor.Isso evita que o cursor seja recalculado sempre que for chamado, o que melhora o desempenho.Isso não permite a modificação do tipo de cursor e as alterações na tabela não são refletidas quando a busca é chamada.

CONJUNTO DE CHAVES:As linhas com cursor são colocadas em uma tabela em tempdb e as alterações nas colunas não-chave são refletidas quando a busca é chamada.No entanto, os novos registros adicionados à tabela não são refletidos.Com o cursor do conjunto de chaves, a instrução SELECT não é avaliada novamente.

DINÂMICO:Todas as alterações na tabela são refletidas no cursor.O cursor é reavaliado quando cada busca é chamada.Ele usa muitos recursos e afeta negativamente o desempenho.

RÁPIDO_AVANÇAR:O cursor é unidirecional, como FORWARD_ONLY, mas especifica o cursor como somente leitura.FORWARD_ONLY é um aumento de desempenho e o cursor não é reavaliado a cada busca.Oferece o melhor desempenho se for adequado para programação.

OTIMISTA:Esta opção pode ser usada para atualizar linhas no cursor.Se uma linha for buscada e atualizada e outra linha for atualizada entre as operações de busca e atualização, a operação de atualização do cursor falhará.Se for utilizado um cursor OPTIMISTIC que possa realizar atualização de linha, ele não deverá ser atualizado por outro processo.

OBSERVAÇÃO:Se cursore não for especificado, o padrão será FORWARD_ONLY.

Para responder às perguntas originais de Mile...

Cursores estáticos de avanço rápido, somente leitura (carinhosamente conhecidos como "Cursor de mangueira de incêndio") são normalmente tão ou mais rápidos que uma tabela temporária equivalente e um loop While porque esse cursor nada mais é do que uma tabela temporária e um loop While que foi otimizado um pouco nos bastidores.

Para acrescentar ao que Eric Z.Beard postou neste tópico e para responder melhor à pergunta de...

"É toda a conversa sobre não usar os cursores realmente para evitar o uso de cursores quando abordagens baseadas em conjuntos estão disponíveis, e o uso de cursores atualizáveis ​​etc."

Sim.Com muito poucas exceções, leva menos tempo e menos código para escrever código baseado em conjunto adequado para fazer a mesma coisa que a maioria dos cursores e tem o benefício adicional de usar muito menos recursos e geralmente executa MUITO mais rápido que um cursor ou loop While.De modo geral e com exceção de certas tarefas administrativas, elas realmente deveriam ser evitadas em favor de um código baseado em conjuntos escrito adequadamente.É claro que existem exceções para cada “regra”, mas, no caso de Cursores, loops While e outras formas de RBAR, a maioria das pessoas pode contar as exceções em uma mão sem usar todos os dedos.;-)

Existe também a noção de "RBAR oculto".Este é um código que parece baseado em conjunto, mas na verdade não é.Este tipo de código "baseado em conjunto" é a razão pela qual certas pessoas adotaram os métodos RBAR e dizem que estão "OK".Por exemplo, resolver o problema do total em execução usando uma subconsulta correlacionada agregada (SUM) com uma desigualdade para construir o total em execução não é realmente baseado em conjunto em meu livro.Em vez disso, é RBAR com esteróides porque, para cada linha calculada, ele tem que "tocar" repetidamente em muitas outras linhas a uma taxa de N*(N+1)/2.Isso é conhecido como "Junção Triangular" e é pelo menos metade tão ruim quanto uma Junção Cartesiana completa (Junção Cruzada ou "Junção Quadrada").

Embora a MS tenha feito algumas melhorias na forma como os Cursores funcionam desde o SQL Server 2005, o termo "Cursor Rápido" ainda é um oxímoro em comparação com o código baseado em conjunto escrito corretamente.Isso também é válido mesmo no Oracle.Trabalhei com a Oracle por apenas 3 anos, mas meu trabalho era fazer melhorias de desempenho no código existente.A maioria das melhorias realmente substanciais foram realizadas quando converti Cursores em código baseado em conjunto.Muitos trabalhos que antes levavam de 4 a 8 horas para serem executados foram reduzidos a minutos e, às vezes, segundos.

Se você quiser um cursor ainda mais rápido que o FAST FORWARD, use um cursor STATIC.Eles são mais rápidos que o FAST FORWARD.Não é extremamente rápido, mas pode fazer a diferença.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top