Eu tenho um procedimento armazenado que contém lógica de negócios. Dentro dele tenho cerca de 1609 variáveis (não me perguntem porquê, é assim que o motor funciona). Eu tento SET
uma variável para o valor concatenado de todas as outras variáveis. Como resultado, durante a criação, recebo o erro:
Msg 8631, nível 17, estado 1, procedimento XXX, linha YYY Erro interno: o limite de pilha do servidor foi atingido. Procure um aninhamento potencialmente profundo em sua consulta e tente simplificá-lo.
Percebi que o erro é devido a quantidade de variáveis que preciso utilizar na SET
operação. Posso executar a tarefa dividindo-a em duas.
A minha pergunta é se existem algumas restrições nesta área? Eu verifiquei, mas não encontrei nenhum.
Verificamos o erro descrito neste KB , mas não é o nosso caso. Não usamos nenhuma CASE
expressão dentro do nosso código. Usamos essa variável temporária para preparar uma lista de valores que devem ser substituídos usando uma função CLR. Atualizamos nosso SQL Server para SP3 CU6 (última atualização), mas ainda encontramos o erro.
Esse erro ocorre com listas de concatenação de atribuição longa
SET
ouSELECT
variável devido à maneira como o SQL Server analisa e vincula esse tipo de instrução - como uma lista aninhada de concatenações de duas entradas.Por exemplo,
SET @V = @W + @X + @Y + @Z
está vinculado a uma árvore da forma:Cada elemento de concatenação após os dois primeiros resulta em um nível extra de aninhamento nessa representação.
A quantidade de espaço de pilha disponível para o SQL Server determina o limite final para esse aninhamento. Quando o limite é excedido, uma exceção é levantada internamente, o que eventualmente resulta na mensagem de erro mostrada acima. Um exemplo de pilha de chamadas de processo quando o erro é lançado é mostrado abaixo:
Reprodução
Este é um limite fundamental devido à forma como várias concatenações são tratadas internamente. Ela afeta as instruções de atribuição
SET
deSELECT
variáveis igualmente.A solução é limitar o número de concatenações executadas em uma única instrução. Isso também costuma ser mais eficiente, já que a compilação de árvores de consulta profunda exige muitos recursos.
Inspirado pela resposta de @Paul , fiz algumas pesquisas e descobri que, embora seja verdade que o espaço da pilha limita o número de concatenações e que o espaço da pilha é uma função da memória disponível e, portanto, varia, os dois pontos a seguir também são verdadeiros :
Primeiro, adaptei o código de teste de Paul para concatenar strings:
Com este teste, o máximo que consegui ao executar em meu laptop não tão bom (apenas 6 GB de RAM) foi:
antes de receber o erro 8631 .
Em seguida, tentei agrupar as concatenações usando parênteses de forma que a operação concatenasse vários grupos de concatenações. Por exemplo:
Fazendo isso, consegui ir muito além dos limites anteriores de 3312 e 3513 variáveis. O código atualizado é:
Os valores máximos (para mim) agora são usar
42
para o primeiroREPLICATE
, usando assim 43 variáveis por grupo, e depois usar762
para o segundoREPLICATE
, usando assim 762 grupos de 43 variáveis cada. O grupo inicial é codificado com duas variáveis.A saída agora mostra que há 32.768 caracteres na
@S
variável. Se eu atualizar o grupo inicial para ser(@A+@A+@A)
em vez de apenas(@A+@A)
, recebo o seguinte erro:Observe que o número do erro é diferente do anterior. Agora é: 8632 . E, tenho esse mesmo limite, independentemente de usar minha instância do SQL Server 2012 ou a instância do SQL Server 2017.
Provavelmente não é coincidência que o limite superior aqui — 32.768 — seja a capacidade máxima de
SMALLINT
(Int16
no .NET) IF começando em0
(o valor máximo é 32.767, mas os arrays em muitas/a maioria das linguagens de programação são baseados em 0).Agora, isso é simplesmente falta de memória em outras palavras, pois a operação do procedimento armazenado feito na memória e os transistores de hardware disponíveis ou memória de página virtual disponível para SQL estão cheios!
Portanto, é basicamente Stack Overflow no SQL Server.
Agora, primeiro tente simplificar o processo, pois sabemos que você precisa de 1609 variáveis,
Mas você precisa de todas as variáveis ao mesmo tempo?
Podemos declarar e usar variáveis quando necessário.
Por exemplo:
Mas se tentarmos isso em um loop adicionando
Observação: isso usará mais CPU e levará um pouco mais de tempo no cálculo.
Agora, isso será lento, mas terá a vantagem de usar menos memória.
Espero que isso ajude, Por favor, poste sua consulta para que possamos entender o cenário exato.
O uso de instruções SELECT em vez de SETs pode melhorar o desempenho e a legibilidade e pode contornar o erro declarado. Então ao invés de:
Você pode fazer:
E defina todos os três valores em uma instrução.