Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

concatenazione di nvarchar / indice / comportamento inesplicabile di nvarchar (max).

TLDR; Questo non è un approccio documentato/supportato per concatenare stringhe tra righe. A volte funziona, ma a volte fallisce anche perché dipende dal piano di esecuzione che ottieni.

Utilizza invece uno dei seguenti approcci garantiti

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

Sfondo

L'articolo della KB già collegato da VanDerNorth include la riga

Il comportamento corretto per una query di concatenazione aggregata non è definito.

ma poi continua a confondere un po' le acque fornendo una soluzione alternativa che sembra indicare che è possibile un comportamento deterministico.

Per ottenere i risultati attesi da una query di concatenazione aggregata, applicare qualsiasi funzione o espressione Transact-SQL alle colonne nell'elenco SELECT anziché nella clausola ORDER BY.

La tua query problematica non applica alcuna espressione alle colonne in ORDER BY clausola.

L'articolo del 2005 Ordinare garanzie in SQL Server... indica

Per motivi di compatibilità con le versioni precedenti, SQL Server fornisce supporto per assegnazioni di tipo SELECT @p =@p + 1 ... ORDER BY nell'ambito più in alto.

Nei piani in cui la concatenazione funziona come previsto, calcola lo scalare con l'espressione [Expr1003] = Scalar Operator([@x]+[Expr1004]) appare sopra l'ordinamento.

Nel piano in cui non funziona, il calcolo scalare appare sotto l'ordinamento. Come spiegato in questo elemento di connessione del 2006 quando l'espressione @x = @x + [msg] appare sotto l'ordinamento viene valutato per ogni riga ma tutte le valutazioni finiscono per utilizzare il valore di preassegnazione di @x . In un altro simile Connect Item del 2006, la risposta di Microsoft parlava di "risoluzione" del problema.

La risposta di Microsoft su tutti gli articoli Connect successivi su questo problema (e ce ne sono molti) affermano che questo semplicemente non è garantito

Esempio 1

non forniamo alcuna garanzia sulla correttezza delle query di concatenazione (come l'utilizzo di assegnazioni di variabili con recupero dei dati in un ordine specifico). L'output della query può cambiare in SQL Server 2008 a seconda della scelta del piano, dei dati nelle tabelle, ecc. Non dovresti fare affidamento su questo lavoro in modo coerente anche se la sintassi ti consente di scrivere un'istruzione SELECT che mescola il recupero delle righe ordinate con l'assegnazione di variabili.

Esempio 2

Il comportamento che stai vedendo è di progettazione. L'utilizzo delle operazioni di assegnazione (concatenazione in questo esempio) nelle query con la clausola ORDER BY ha un comportamento non definito. Questo può cambiare da una versione all'altra o anche all'interno di una particolare versione del server a causa di modifiche nel piano di query. Non puoi fare affidamento su questo comportamento anche se esistono soluzioni alternative. Per ulteriori dettagli, vedere l'articolo della Knowledge Base riportato di seguito:
http://support.microsoft.com/kb/287515 L'UNICO meccanismo garantito è il seguente:

  1. Usa il cursore per scorrere le righe in un ordine specifico e concatenare i valori
  2. Utilizzare per query xml con ORDER BY per generare i valori concatenati
  3. Utilizza l'aggregato CLR (questo non funzionerà con la clausola ORDER BY)

Esempio 3

Il comportamento che stai vedendo è in realtà di progettazione. Questo ha a che fare con SQL che è un linguaggio di manipolazione degli insiemi. Non è garantito che tutte le espressioni in SELECTlist (e questo include anche le assegnazioni) vengano eseguite esattamente una volta per ogni riga di output. In effetti, SQL queryoptimizer si sforza di eseguirli il minor numero di volte possibile. Questo darà i risultati attesi quando si calcola il valore della variabile in base ad alcuni dati nelle tabelle, ma quando il valore che si sta assegnando dipende dal valore precedente della stessa variabile, i risultati potrebbero essere piuttosto inaspettati. Se Query Optimizer sposta l'espressione in una posizione diversa nell'albero delle query, potrebbe essere valutata meno volte (o solo una volta, come in uno dei tuoi esempi). Questo è il motivo per cui non è consigliabile utilizzare le assegnazioni di tipo "iterazione" per calcolare i valori aggregati. Troviamo che le soluzioni alternative basate su XML... di solito funzionano bene per i clienti

Esempio 4

Anche senza ORDER BY, non garantiamo che @var =@var + produrrà il valore concatenato per qualsiasi istruzione che interessa più righe. Il lato destro dell'espressione può essere valutato una o più volte durante l'esecuzione della query e il comportamento, come ho detto, dipende dal piano.

Esempio 5

L'assegnazione di variabili con l'istruzione SELECT è una sintassi proprietaria (solo T-SQL) in cui il comportamento non è definito o dipende dal piano se vengono prodotte più righe. Se è necessario eseguire la concatenazione di stringhe, utilizzare un aggregato SQLCLR o una concatenazione basata su query FOR XML o altri metodi relazionali.