Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

nvarchar конкатенация / индекс / nvarchar(max) необяснимо поведение

TLDR; Това не е документиран/поддържан подход за конкатенация на низове между редове. Понякога работи, но понякога се проваля, тъй като зависи какъв план за изпълнение ще получите.

Вместо това използвайте един от следните гарантирани подходи

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)') 

Фон

Статията от KB, вече свързана от VanDerNorth, включва реда

Правилното поведение за заявка за обобщена конкатенация е недефинирано.

но след това продължава да замъглява малко водата, като предоставя заобиколно решение, което изглежда показва, че е възможно детерминирано поведение.

За да постигнете очакваните резултати от заявка за обединяване на агрегати, приложете която и да е функция или израз на Transact-SQL към колоните в списъка SELECT, а не в клаузата ORDER BY.

Вашата проблемна заявка не прилага никакви изрази към колони в ORDER BY клауза.

Статията от 2005 г. Гарантии за поръчка в SQL Server... казва

От съображения за обратна съвместимост SQL Server предоставя поддръжка за присвояване от тип SELECT @p =@p + 1 ... ORDER BY в най-горния обхват.

В плановете, където конкатенацията работи както сте очаквали, изчисли скалар с израза [Expr1003] = Scalar Operator([@x]+[Expr1004]) се появява над сортирането.

В плана, където не работи, изчислителният скалар се появява под сортирането. Както е обяснено в този елемент за свързване от 2006 г., когато изразът @x = @x + [msg] се появява под сортирането, което се оценява за всеки ред, но всички оценки в крайна сметка използват стойността за предварително присвояване на @x . В друг подобен елемент за свързване от 2006 г. отговорът от Microsoft говори за „поправяне“ на проблема.

Отговорът на Microsoft за всички по-късни елементи на Connect по този проблем (а има много) посочва, че това просто не е гарантирано

Пример 1

ние не даваме никакви гаранции за коректността на заявките за конкатенация (като използване на присвояване на променливи с извличане на данни в специфичен ред). Резултатът от заявката може да се промени в SQL Server 2008 в зависимост от избора на план, данните в таблиците и т.н. Не трябва да разчитате на това да работи последователно, въпреки че синтаксисът ви позволява да напишете израз SELECT, който смесва извличане на подредени редове с присвояване на променлива.

Пример 2

Поведението, което виждате, е по замисъл. Използването на операции за присвояване (конкатенация в този пример) в заявки с клауза ORDER BY има недефинирано поведение. Това може да се промени от версия до версия или дори в рамките на определена версия на сървъра поради промени в плана на заявката. Не можете да разчитате на това поведение, дори ако има заобиколни решения. Вижте статията от KB по-долу за повече подробности:
http://support.microsoft.com/kb/287515 ЕДИНСТВЕНИЯТ гарантиран механизъм е следният:

  1. Използвайте курсора, за да преминете през редовете в определен ред и да свържете стойностите
  2. Използвайте за xml заявка с ORDER BY, за да генерирате свързаните стойности
  3. Използвайте CLR агрегат (това няма да работи с клауза ORDER BY)

Пример 3

Поведението, което виждате, всъщност е по замисъл. Това е свързано с това, че SQL е език за манипулиране на набори. Всички изрази в SELECTlist (и това включва и присвояването) не е гарантирано, че ще бъдат изпълнени точно веднъж за всеки изходен ред. Всъщност SQL queryoptimizer се опитва да ги изпълни възможно най-малко пъти. Това ще даде очаквани резултати, когато изчислявате стойността на променливата въз основа на някои данни в таблиците, но когато стойността, която присвоявате, зависи от предишната стойност на същата променлива, резултатите може да са доста неочаквани. Ако оптимизаторът на заявки премести израза на друго място в дървото на заявките, той може да бъде оценен по-малко пъти (или само веднъж, както в един от вашите примери). Ето защо не препоръчваме да използвате присвояването на типа "итерация" за изчисляване на обобщени стойности. Откриваме, че базираните на XML решения ... обикновено работят добре за клиентите

Пример 4

Дори и без ORDER BY, ние не гарантираме, че @var =@var + ще произведе конкатенираната стойност за всеки израз, който засяга множество редове. Дясната страна на израза може да бъде оценена веднъж или няколко пъти по време на изпълнение на заявката и поведението, както казах, зависи от плана.

Пример 5

Присвояването на променлива с израза SELECT е собствен синтаксис (само за T-SQL), където поведението е недефинирано или зависи от плана, ако се генерират няколко реда. Ако трябва да направите конкатенация на низове, тогава използвайте SQLCLR агрегат или FOR XML базирана на заявка конкатенация или други релационни методи.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Съвети за производителност на SQL Server от Брент Озар и Пинал Дейв

  2. Как да зададете цвета на лентата на състоянието в SSMS за различни екземпляри на SQL сървър - SQL Server / TSQL урок, част 6

  3. Как да създадете ограничение на външния ключ с опция ON DELETE SET NULL в SQL Server - SQL Server / TSQL Урок, част 81

  4. 4 невероятни ресурси за наблюдение на SQL Server за администратори на бази данни

  5. Въведение във вградените функции с таблично стойности (ITVF) в SQL Server