sql >> Base de Datos >  >> RDS >> Sqlserver

concatenación nvarchar / índice / nvarchar (max) comportamiento inexplicable

TLDR; Este no es un enfoque documentado/compatible para concatenar cadenas entre filas. A veces funciona, pero también a veces falla, ya que depende del plan de ejecución que obtenga.

En su lugar, utilice uno de los siguientes enfoques garantizados

Servidor SQL 2017+

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

Servidor SQL 2005+

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

Antecedentes

El artículo de KB ya vinculado por VanDerNorth incluye la línea

El comportamiento correcto para una consulta de concatenación agregada no está definido.

pero luego enturbia un poco las aguas al proporcionar una solución alternativa que parece indicar que es posible un comportamiento determinista.

Para lograr los resultados esperados de una consulta de concatenación agregada, aplique cualquier función o expresión Transact-SQL a las columnas en la lista SELECT en lugar de en la cláusula ORDER BY.

Su consulta problemática no aplica ninguna expresión a las columnas en ORDER BY cláusula.

El artículo de 2005 Pedidos de garantías en SQL Server... afirma

Por motivos de compatibilidad con versiones anteriores, SQL Server admite asignaciones de tipo SELECT @p =@p + 1 ... ORDER BY en el ámbito superior.

En los planes donde la concatenación funciona como esperaba, calcule el escalar con la expresión [Expr1003] = Scalar Operator([@x]+[Expr1004]) aparece encima de la ordenación.

En el plan donde no funciona, el escalar de cómputo aparece debajo de la ordenación. Como se explica en este elemento de conexión de 2006 cuando la expresión @x = @x + [msg] aparece debajo del orden que se evalúa para cada fila, pero todas las evaluaciones terminan usando el valor de preasignación de @x . En otro Connect Item similar de 2006, la respuesta de Microsoft hablaba de "arreglar" el problema.

La respuesta de Microsoft sobre todos los elementos posteriores de Connect sobre este problema (y hay muchos) indica que esto simplemente no está garantizado

Ejemplo 1

no garantizamos la exactitud de las consultas de concatenación (como el uso de asignaciones de variables con recuperación de datos en un orden específico). El resultado de la consulta puede cambiar en SQL Server 2008 dependiendo de la elección del plan, los datos en las tablas, etc. No debe confiar en que esto funcione de manera consistente aunque la sintaxis le permita escribir una instrucción SELECT que mezcle la recuperación de filas ordenadas con la asignación de variables.

Ejemplo 2

El comportamiento que está viendo es por diseño. El uso de operaciones de asignación (concatenación en este ejemplo) en consultas con la cláusula ORDER BY tiene un comportamiento indefinido. Esto puede cambiar de una versión a otra o incluso dentro de una versión de servidor en particular debido a cambios en el plan de consulta. No puede confiar en este comportamiento incluso si existen soluciones alternativas. Consulte el siguiente artículo de KB para obtener más detalles:
http://support.microsoft.com/kb/287515 Los ÚNICOS mecanismos garantizados son los siguientes:

  1. Utilice el cursor para recorrer las filas en un orden específico y concatenar los valores
  2. Usar para consulta xml con ORDER BY para generar los valores concatenados
  3. Usar agregado CLR (esto no funcionará con la cláusula ORDER BY)

Ejemplo 3

El comportamiento que está viendo es en realidad por diseño. Esto tiene que ver con que SQL sea un lenguaje de manipulación de conjuntos. No se garantiza que todas las expresiones en la lista SELECT (y esto también incluye las asignaciones) se ejecuten exactamente una vez para cada fila de salida. De hecho, SQL queryoptimizer se esfuerza por ejecutarlos la menor cantidad de veces posible. Esto dará los resultados esperados cuando calcule el valor de la variable con base en algunos datos de las tablas, pero cuando el valor que esté asignando dependa del valor anterior de la misma variable, los resultados pueden ser bastante inesperados. Si el optimizador de consultas mueve la expresión a un lugar diferente en el árbol de consultas, es posible que se evalúe menos veces (o solo una vez, como en uno de sus ejemplos). Esta es la razón por la que no recomendamos utilizar las asignaciones de tipo "iteración" para calcular valores agregados. Encontramos que las soluciones alternativas basadas en XML... por lo general funcionan bien para los clientes

Ejemplo 4

Incluso sin ORDER BY, no garantizamos que @var =@var +produzca el valor concatenado para cualquier declaración que afecte varias filas. El lado derecho de la expresión se puede evaluar una o varias veces durante la ejecución de la consulta y, como dije, el comportamiento depende del plan.

Ejemplo 5

La asignación de variables con la declaración SELECT es una sintaxis propietaria (solo T-SQL) donde el comportamiento no está definido o depende del plan si se producen varias filas. Si necesita realizar la concatenación de cadenas, utilice un agregado SQLCLR o una concatenación basada en consultas FOR XML u otros métodos relacionales.