Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

nvarchar konkatenacja / index / nvarchar(max) niewytłumaczalne zachowanie

TLDR; Nie jest to udokumentowane/obsługiwane podejście do łączenia ciągów w wierszach. Czasami działa, ale czasami zawodzi, ponieważ zależy to od tego, jaki plan wykonania otrzymasz.

Zamiast tego użyj jednego z poniższych gwarantowanych podejść

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

Tło

Artykuł KB już połączony przez VanDerNorth zawiera linię

Prawidłowe zachowanie zapytania agregującego konkatenacji jest niezdefiniowane.

ale potem nieco zmąci wody, zapewniając obejście, które wydaje się wskazywać, że możliwe jest zachowanie deterministyczne.

Aby uzyskać oczekiwane wyniki z zapytania agregującego konkatenacji, zastosuj dowolną funkcję lub wyrażenie Transact-SQL do kolumn na liście SELECT, a nie do klauzuli ORDER BY.

Twoje problematyczne zapytanie nie stosuje żadnych wyrażeń do kolumn w ORDER BY klauzula.

Artykuł 2005 Gwarancje zamawiania w SQL Server... zawiera stwierdzenie

Ze względu na kompatybilność wsteczną SQL Server zapewnia obsługę przypisań typu SELECT @p =@p + 1 ... ORDER BY w najwyższym zakresie.

W planach, w których konkatenacja działa zgodnie z oczekiwaniami, skalar obliczeniowy z wyrażeniem [Expr1003] = Scalar Operator([@x]+[Expr1004]) pojawia się nad sortowaniem.

W planie, w którym nie działa, skalar obliczeniowy pojawia się pod sortowaniem. Jak wyjaśniono w tym elemencie connect z 2006 roku, gdy wyrażenie @x = @x + [msg] pojawia się poniżej sortowania, w którym jest oceniany dla każdego wiersza, ale wszystkie oceny kończą się użyciem wartości wstępnego przypisania @x . W innym podobnym elemencie Connect z 2006 roku odpowiedź Microsoft mówiła o „naprawieniu” problemu.

Odpowiedź Microsoft dotycząca wszystkich późniejszych elementów Connect dotyczących tego problemu (a jest ich wiele) stwierdza, że ​​po prostu nie jest to gwarantowane

Przykład 1

nie dajemy żadnych gwarancji na poprawność konkatenacji zapytań (np. używanie przypisań zmiennych z pobieraniem danych w określonej kolejności). Wynik zapytania może się zmienić w SQL Server 2008 w zależności od wyboru planu, danych w tabelach itp. Nie powinieneś polegać na tym, że działa to konsekwentnie, nawet jeśli składnia pozwala na napisanie instrukcji SELECT, która łączy pobieranie uporządkowanych wierszy ze zmiennym przypisaniem.

Przykład 2

Zachowanie, które widzisz, jest zgodne z projektem. Używanie operacji przypisania (w tym przykładzie konkatenacji) w zapytaniach z klauzulą ​​ORDER BY ma niezdefiniowane zachowanie. Może się to zmieniać od wydania do wydania lub nawet w ramach określonej wersji serwera z powodu zmian w planie zapytania. Nie można polegać na tym zachowaniu, nawet jeśli istnieją obejścia. Więcej informacji znajdziesz w poniższym artykule na temat bazy wiedzy:
http://support.microsoft.com/kb/287515 JEDYNYM gwarantowanym mechanizmem są następujące:

  1. Użyj kursora, aby przejść przez wiersze w określonej kolejności i połączyć wartości
  2. Użyj dla zapytania XML z ORDER BY, aby wygenerować połączone wartości
  3. Użyj agregatu CLR (nie będzie działać z klauzulą ​​ORDER BY)

Przykład 3

Zachowanie, które widzisz, jest w rzeczywistości zgodne z projektem. Ma to związek z tym, że SQL jest językiem manipulacji zbiorami. Nie gwarantuje się, że wszystkie wyrażenia z listy SELECTlist (w tym również przypisania) zostaną wykonane dokładnie raz dla każdego wiersza wyjściowego. W rzeczywistości queryoptimizer SQL stara się wykonać je jak najmniej razy. Daje to oczekiwane wyniki, gdy obliczasz wartość zmiennej na podstawie niektórych danych w tabelach, ale gdy przypisywana wartość zależy od poprzedniej wartości tej samej zmiennej, wyniki mogą być dość nieoczekiwane. Jeśli optymalizator zapytań przeniesie wyrażenie w inne miejsce w drzewie zapytań, może ono zostać ocenione mniej razy (lub tylko raz, jak w jednym z przykładów). Dlatego nie zalecamy używania przypisań typu „iteracja” do obliczania wartości zagregowanych. Uważamy, że obejścia oparte na XML ... zwykle działają dobrze dla klientów

Przykład 4

Nawet bez ORDER BY nie gwarantujemy, że @var =@var + wygeneruje połączoną wartość dla każdej instrukcji, która ma wpływ na wiele wierszy. Prawa strona wyrażenia może być oceniana raz lub wiele razy podczas wykonywania zapytania, a zachowanie, jak powiedziałem, jest zależne od planu.

Przykład 5

Przypisanie zmiennej z instrukcją SELECT jest zastrzeżoną składnią (tylko T-SQL), w której zachowanie jest niezdefiniowane lub zależne od planu, jeśli tworzonych jest wiele wierszy. Jeśli musisz wykonać łączenie ciągów, użyj agregacji SQLCLR lub łączenia opartego na zapytaniu FOR XML lub innych metod relacyjnych.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak mogę uzyskać sumę wielu wartości daty i godziny?

  2. Błąd serwera SQL 206:konflikt typu operandu

  3. Jak zamienić wartości null na nieznane w instrukcji Select w SQL Server — samouczek SQL Server / TSQL, część 111

  4. Pobierz listę wszystkich pustych i nie zerowych kolumn w bazie danych SQL Server — samouczek SQL Server / T-SQL, część 53

  5. Konwertuj datę na inną strefę czasową w SQL Server