Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

nvarchar-Verkettung / Index / nvarchar (max) unerklärliches Verhalten

TLDR; Dies ist kein dokumentierter/unterstützter Ansatz zum Verketten von Zeichenfolgen über Zeilen hinweg. Es funktioniert manchmal, aber manchmal schlägt es auch fehl, da es davon abhängt, welchen Ausführungsplan Sie erhalten.

Verwenden Sie stattdessen einen der folgenden garantierten Ansätze

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

Hintergrund

Der bereits von VanDerNorth verlinkte KB-Artikel enthält die Zeile

Das richtige Verhalten für eine aggregierte Verkettungsabfrage ist nicht definiert.

fährt dann aber fort, das Wasser ein wenig zu trüben, indem es eine Problemumgehung bereitstellt, die darauf hindeutet, dass deterministisches Verhalten möglich ist.

Um die erwarteten Ergebnisse einer Aggregatverkettungsabfrage zu erzielen, wenden Sie eine beliebige Transact-SQL-Funktion oder einen beliebigen Ausdruck auf die Spalten in der SELECT-Liste statt in der ORDER BY-Klausel an.

Ihre problematische Abfrage wendet keine Ausdrücke auf Spalten in ORDER BY an Klausel.

Der Artikel Ordering Guarantees in SQL Server... von 2005 besagt

Aus Gründen der Abwärtskompatibilität bietet SQL Server Unterstützung für Zuweisungen vom Typ SELECT @p =@p + 1 ... ORDER BY im obersten Bereich.

In den Plänen, in denen die Verkettung wie erwartet funktioniert, berechnet der Skalar mit dem Ausdruck [Expr1003] = Scalar Operator([@x]+[Expr1004]) erscheint oberhalb der Sortierung.

In dem Plan, in dem es nicht funktioniert, wird der Compute-Skalar unter der Sortierung angezeigt. Wie in diesem Connect-Item von 2006 erklärt, wenn der Ausdruck @x = @x + [msg] unterhalb der Sortierung erscheint, wird es für jede Zeile ausgewertet, aber alle Auswertungen verwenden am Ende den Vorabzuweisungswert von @x . In einem anderen ähnlichen Connect Item aus dem Jahr 2006 sprach die Antwort von Microsoft von einer "Behebung" des Problems.

Die Microsoft-Antwort zu allen späteren Connect-Elementen zu diesem Problem (und es gibt viele) gibt an, dass dies einfach nicht garantiert wird

Beispiel 1

Wir übernehmen keine Garantien für die Korrektheit von Verkettungsabfragen (wie die Verwendung von Variablenzuweisungen mit Datenabruf in einer bestimmten Reihenfolge). Die Abfrageausgabe kann sich in SQL Server 2008 je nach gewähltem Plan, Daten in den Tabellen usw. ändern. Sie sollten sich nicht darauf verlassen, dass dies konsistent funktioniert, obwohl die Syntax es Ihnen erlaubt, eine SELECT-Anweisung zu schreiben, die den Abruf geordneter Zeilen mit einer Variablenzuweisung mischt.

Beispiel 2

Das Verhalten, das Sie sehen, ist beabsichtigt. Die Verwendung von Zuweisungsoperationen (in diesem Beispiel Verkettung) in Abfragen mit der ORDER BY-Klausel hat ein undefiniertes Verhalten. Dies kann sich aufgrund von Änderungen im Abfrageplan von Release zu Release oder sogar innerhalb einer bestimmten Serverversion ändern. Sie können sich nicht auf dieses Verhalten verlassen, auch wenn es Workarounds gibt. Weitere Einzelheiten finden Sie im folgenden KB-Artikel:
http://support.microsoft.com/kb/287515 Der EINZIGE garantierte Mechanismus ist der Folgende:

  1. Verwenden Sie den Cursor, um die Zeilen in einer bestimmten Reihenfolge zu durchlaufen und die Werte zu verketten
  2. Für XML-Abfragen mit ORDER BY verwenden, um die verketteten Werte zu generieren
  3. CLR-Aggregat verwenden (dies funktioniert nicht mit der ORDER BY-Klausel)

Beispiel 3

Das Verhalten, das Sie sehen, ist eigentlich beabsichtigt. Dies hat damit zu tun, dass SQL eine Satzmanipulationssprache ist. Es ist nicht garantiert, dass alle Ausdrücke in der SELECT-Liste (und dazu gehören auch Zuweisungen) genau einmal für jede Ausgabezeile ausgeführt werden. Tatsächlich versucht der SQL-Abfrageoptimierer, sie so wenig wie möglich auszuführen. Dies führt zu den erwarteten Ergebnissen, wenn Sie den Wert der Variablen basierend auf einigen Daten in den Tabellen berechnen, aber wenn der Wert, den Sie zuweisen, vom vorherigen Wert derselben Variablen abhängt, können die Ergebnisse ziemlich unerwartet sein. Wenn der Abfrageoptimierer den Ausdruck an eine andere Stelle in der Abfragestruktur verschiebt, wird er möglicherweise seltener ausgewertet (oder nur einmal, wie in einem Ihrer Beispiele). Aus diesem Grund empfehlen wir nicht, Zuweisungen vom Typ "Iteration" zu verwenden, um aggregierte Werte zu berechnen. Wir finden, dass XML-basierte Problemumgehungen ... normalerweise gut für die Kunden funktionieren

Beispiel 4

Auch ohne ORDER BY garantieren wir nicht, dass @var =@var +den verketteten Wert für jede Anweisung erzeugt, die mehrere Zeilen betrifft. Die rechte Seite des Ausdrucks kann während der Abfrageausführung entweder einmal oder mehrmals ausgewertet werden, und das Verhalten ist, wie gesagt, planabhängig.

Beispiel 5

Die Variablenzuweisung mit der SELECT-Anweisung ist eine proprietäre Syntax (nur T-SQL), bei der das Verhalten undefiniert oder planabhängig ist, wenn mehrere Zeilen erzeugt werden. Wenn Sie die Zeichenfolgenverkettung durchführen müssen, verwenden Sie ein SQLCLR-Aggregat oder eine auf FOR XML-Abfragen basierende Verkettung oder andere relationale Methoden.