Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

concatenação nvarchar / índice / nvarchar (max) comportamento inexplicável


TLDR; Esta não é uma abordagem documentada/com suporte para concatenar strings entre linhas. Às vezes funciona, mas às vezes também falha, pois depende do plano de execução que você obtém.

Em vez disso, use uma das seguintes abordagens garantidas

SQL Server 2017+
SELECT @a = STRING_AGG([msg], '') WITHIN GROUP (ORDER BY [priority] ASC)
FROM bla
where   autofix = 0

SQL Server 2005+
SELECT @a = (SELECT [msg] + ''
             FROM   bla
             WHERE  autofix = 0
             ORDER  BY [priority] ASC
             FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)') 

Plano de fundo

O artigo da KB já vinculado por VanDerNorth inclui a linha

O comportamento correto para uma consulta de concatenação agregada é indefinido.

mas depois turva as águas um pouco, fornecendo uma solução alternativa que parece indicar que o comportamento determinístico é possível.

Para obter os resultados esperados de uma consulta de concatenação agregada, aplique qualquer função ou expressão Transact-SQL às colunas da lista SELECT em vez da cláusula ORDER BY.

Sua consulta problemática não aplica nenhuma expressão a colunas no ORDER BY cláusula.

O artigo de 2005 Encomendar garantias no SQL Server...

Por motivos de compatibilidade com versões anteriores, o SQL Server oferece suporte para atribuições do tipo SELECT @p =@p + 1 ... ORDER BY no escopo mais alto.

Nos planos em que a concatenação funciona como você esperava, a computação escalar com a expressão [Expr1003] = Scalar Operator([@x]+[Expr1004]) aparece acima da classificação.

No plano em que não funciona, o escalar de computação aparece abaixo da classificação. Conforme explicado neste item de conexão de 2006 quando a expressão @x = @x + [msg] aparece abaixo da classificação é avaliada para cada linha, mas todas as avaliações acabam usando o valor de pré-atribuição de @x . Em outro Connect Item semelhante de 2006, a resposta da Microsoft falou em "corrigir" o problema.

A resposta da Microsoft em todos os itens do Connect posteriores sobre esse problema (e há muitos) afirmam que isso simplesmente não é garantido

Exemplo 1

não garantimos a exatidão das consultas de concatenação (como usar atribuições de variáveis ​​com recuperação de dados em uma ordem específica). A saída da consulta pode mudar no SQL Server 2008 dependendo da escolha do plano, dados nas tabelas etc. Você não deve confiar nisso trabalhando de forma consistente, embora a sintaxe permita escrever uma instrução SELECT que misture recuperação de linhas ordenadas com atribuição de variável.

Exemplo 2

O comportamento que você está vendo é por design. O uso de operações de atribuição (concatenação neste exemplo) em consultas com a cláusula ORDER BY tem comportamento indefinido. Isso pode mudar de versão para versão ou até mesmo dentro de uma versão de servidor específica devido a alterações no plano de consulta. Você não pode confiar nesse comportamento mesmo se houver soluções alternativas. Consulte o artigo KB abaixo para obter mais detalhes:
http://support.microsoft.com/kb/287515 O ÚNICO mecanismo garantido é o seguinte:
  1. Use o cursor para percorrer as linhas em uma ordem específica e concatenar os valores
  2. Use para consulta xml com ORDER BY para gerar os valores concatenados
  3. Use o agregado CLR (isso não funcionará com a cláusula ORDER BY)

Exemplo 3

O comportamento que você está vendo é realmente por design. Isso tem a ver com o SQL sendo uma linguagem de manipulação de conjuntos. Não é garantido que todas as expressões na SELECTlist (e isso inclui atribuições também) sejam executadas exatamente uma vez para cada linha de saída. Na verdade, o SQL queryoptimizer se esforça para executá-los o menor número de vezes possível. Isso dará os resultados esperados quando você estiver calculando o valor da variável com base em alguns dados nas tabelas, mas quando o valor que você está atribuindo depende do valor anterior da mesma variável, os resultados podem ser bastante inesperados. Se o otimizador de consulta mover a expressão para um local diferente na árvore de consulta, ela poderá ser avaliada menos vezes (ou apenas uma vez, como em um de seus exemplos). É por isso que não recomendamos usar as atribuições do tipo "iteração" para calcular valores agregados. Achamos que as soluções alternativas baseadas em XML... geralmente funcionam bem para os clientes

Exemplo 4

Mesmo sem ORDER BY, não garantimos que @var =@var + produzirá o valor concatenado para qualquer instrução que afete várias linhas. O lado direito da expressão pode ser avaliado uma ou várias vezes durante a execução da consulta e o comportamento, como eu disse, depende do plano.

Exemplo 5

A atribuição de variável com a instrução SELECT é uma sintaxe proprietária (somente T-SQL) em que o comportamento é indefinido ou dependente do plano se várias linhas forem produzidas. Se você precisar fazer a concatenação de strings, use um agregado SQLCLR ou uma concatenação baseada em consulta FOR XML ou outros métodos relacionais.