Normalmente evito operações de cursor como a placa, no entanto, encontrei meu primeiro problema em que o uso de um cursor supera uma consulta. Então sou forçado a usá-lo.
Eu criei um procedimento armazenado de relatório elaborado que é usado por muitos usuários. Eu uso um cursor para iterar uma lista e inserir dados em um @TEMP_TABLE, finalmente selecionando a tabela temporária como conjunto de resultados.
O cursor é usado da seguinte maneira:
DECLARE HIGHLIGHTS_REPORT_CURSOR CURSOR LOCAL FOR
SELECT RowID, UserID,GradeID,ClassID,MenuSetID,CurrentSequence FROM @DATA
OPEN HIGHLIGHTS_REPORT_CURSOR
FETCH NEXT FROM HIGHLIGHTS_REPORT_CURSOR INTO @RowID,@UserID,@GradeID,@ClassID,@MenuSetID,@CurrentSequence
WHILE(@@FETCH_STATUS=0)BEGIN
...
FETCH NEXT FROM HIGHLIGHTS_REPORT_CURSOR INTO @RowID, @UserID,@GradeID,@ClassID,@MenuSetID,@CurrentSequence
END
CLOSE HIGHLIGHTS_REPORT_CURSOR
DEALLOCATE HIGHLIGHTS_REPORT_CURSOR
Então, está tudo bem, o relatório passou pelo controle de qualidade e sem problemas até que o usuário baseou-se agora, estou recebendo logs de exceções enviados para mim esporadicamente com o seguinte erro:
Server Log Reference ID : b91a8f4a-b944-4355-bba3-2855fd126c2b
Message : A cursor with the name 'HIGHLIGHTS_REPORT_CURSOR' does not exist.
Source : HighlightsReport
Stack Trace : at System.Data.SqlClient.SqlConnection.OnError(SqlExceptionexception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
Posso afirmar com 100% de confiança que não há outro cursor com o mesmo nome. Não há CLOSE\DEALLOCATE CURSOR
chamadas erradas entre BEGIN
e END
.
Aprendi que o SQL Server padroniza a propriedade de banco de dados "cursor padrão" GLOBAL
durante a instalação. Como os procedimentos de relatório são os únicos SPs em que os cursores são usados, alterei o "cursor padrão" para LOCAL
.
Isso não ajudou:(Talvez "cursor padrão" seja uma configuração que exija uma reinicialização. Em vez de reiniciar, usei o LOCAL
atributo da CURSOR
declaração.
Isso garante que meu cursor tenha como escopo o SP, mas ainda receba o erro acima???
Estou começando a pensar que este é um problema de simultaneidade com várias conexões atingindo o SP ao mesmo tempo. Isso explicaria o comportamento esporádico e por que isso não foi detectado durante o controle de qualidade.
Seria possível que duas conexões chamassem o procedimento aproximadamente ao mesmo tempo em que uma conexão atinge o DEALLOCATE HIGHLIGHTS_REPORT_CURSOR
logo antes da segunda conexão atingir o FETCH NEXT FROM HIGHLIGHTS_REPORT_CURSOR INTO
bloco?
Presumi que usar LOCAL garantisse que cada conexão teria sua cópia, mas parece que não é o caso? Alguma ideia?
Atualizada. Isso é bizarro, recebi três mensagens de exceção empilhadas em uma. Gostaria de saber se uma conexão de cliente chamando o mesmo sp em rápida sucessão causa algo semelhante.
Server Log Reference ID : b91a8f4a-b944-4355-bba3-2855fd126c2b
Message : A cursor with the name 'HIGHLIGHTS_REPORT_CURSOR' does not exist.
A cursor with the name 'HIGHLIGHTS_REPORT_CURSOR' does not exist.
A cursor with the name 'HIGHLIGHTS_REPORT_CURSOR' does not exist.
Source : HighlightsReport
Atualização 2 A parte que me faz pensar que este é um problema de simultaneidade do usuário é o fato de que esses erros sempre vêm em grupos de dois. Recebo um relatório de exceção com o erro mencionado acima de dois usuários diferentes ao mesmo tempo.
Portanto, isso foi uma esquisitice e ainda não cheguei às porcas e parafusos. Em poucas palavras, habilitei o MARS nas conexões do SQL Server há algum tempo para corrigir um problema que estava tendo com as conexões do contêiner IoC.
Isso corrigiu uma exceção semelhante a "Você deve fechar o comando atual yaddayaddayadda". No entanto, vários conjuntos de resultados ativos parecem levar a um problema de simultaneidade no meu caso. Na verdade, consertei o problema original que deveria ter sido corrigido em vez de habilitar o MARS. A correção foi criar implicitamente um novo contexto de dados por chamada.