Wie verwende ich COALESCE mit mehreren Zeilen und ohne vorangestelltes Komma?

27

Ich versuche folgendes zu erreichen:

California | Los Angeles, San Francisco, Sacramento
Florida    | Jacksonville, Miami

Leider bekomme ich ", Los Angeles, San Francisco, Sacramento, Jacksonville, Miami"

Ich kann mit der Funktion STUFF meine gewünschten Ergebnisse erzielen, habe mich aber gefragt, ob es eine sauberere Möglichkeit gibt, dies mit COALESCE zu tun?

STATE       | CITY
California  | San Francisco
California  | Los Angeles
California  | Sacramento
Florida     | Miami
Florida     | Jacksonville 


DECLARE @col NVARCHAR(MAX);
SELECT @col= COALESCE(@col, '') + ',' + city
FROM tbl where city = 'California';
SELECT @col;

Vielen Dank

user2732180
quelle

Antworten:

45

Dies könnte der sauberere Ansatz sein, den Sie suchen. Überprüfen Sie grundsätzlich, ob die Variable noch initialisiert wurde. Wenn dies nicht der Fall ist, setzen Sie es auf die leere Zeichenfolge und fügen Sie die erste Stadt hinzu (kein führendes Komma). Wenn dies der Fall ist, fügen Sie ein Komma und dann die Stadt hinzu.

DECLARE @col nvarchar(MAX);
SELECT @col = COALESCE(@col + ',', '') + city
  FROM dbo.tbl WHERE state = 'California';

Dies funktioniert natürlich nur zum Auffüllen einer Variablen pro Status. Wenn Sie die Liste für jeden Status einzeln abrufen, gibt es eine bessere Lösung auf einen Schlag:

SELECT [state], cities = STUFF((
    SELECT N', ' + city FROM dbo.tbl
    WHERE [state] = x.[state]
    FOR XML PATH(''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 2, N'')
FROM dbo.tbl AS x
GROUP BY [state]
ORDER BY [state];

Ergebnisse:

state       cities
----------  --------------------------------------
California  San Francisco, Los Angeles, Sacramento  
Florida     Miami, Jacksonville

So bestellen Sie nach dem Namen der Stadt in jedem Bundesland:

SELECT [state], cities = STUFF((
    SELECT N', ' + city FROM dbo.tbl
    WHERE [state] = x.[state]
    ORDER BY city
    FOR XML PATH(''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 2, N'')
FROM dbo.tbl AS x
GROUP BY [state]
ORDER BY [state];

In Azure SQL-Datenbank oder SQL Server 2017+ können Sie die neue STRING_AGG()Funktion verwenden :

SELECT [state], cities = STRING_AGG(city, N', ')
  FROM dbo.tbl
  GROUP BY [state]
  ORDER BY [state];

Und geordnet nach Städtenamen:

SELECT [state], cities = STRING_AGG(city, N', ') 
                         WITHIN GROUP (ORDER BY city)
  FROM dbo.tbl
  GROUP BY [state]
  ORDER BY [state];
Aaron Bertrand
quelle
Vielen Dank, Aaron. Meine aktuelle Lösung ist nahezu identisch mit Ihrer, außer dass ich DISTINCT anstelle von GROUP BY verwende.
User2732180
2
@ user2732180 Sie sollten GROUP BY verwenden, da die Verkettung mit größerer Wahrscheinlichkeit einmal pro Status ausgeführt wird. Mit DISTINCT wird beispielsweise für jede Instanz von Kalifornien dieselbe Verkettung angewendet, und erst dann wird die gesamte Arbeit, die zur Erstellung dieser Duplikate geleistet wurde, verworfen.
Aaron Bertrand
6

Nur um Aarons Antwort oben zu ergänzen ...

Beachten Sie, dass ORDER BYmöglicherweise nur das letzte Element in Ihre Abfrage eingeschlossen wird. In meinem Fall war ich nicht gruppiert, also nicht sicher, ob das einen Unterschied macht. Ich verwende SQL 2014. In meinem Fall habe ich so etwas wie value1, value2, value3 ... aber mein Ergebnis in der Variablen war nur value3.


Aaron kommentierte, um zu sagen:

Dies wurde bei Connect mindestens viermal gemeldet:

  1. In Variable Verkettung und Reihenfolge nach Filtern Ergebnisse (wie wo Bedingung)
  2. (n) Die Erstellung von varchar aus ResultSet schlägt fehl, wenn ORDER BY hinzugefügt wird
  3. Das Zuweisen einer lokalen Variablen aus einem geordneten SELECT mit CROSS APPLYs und einer Tabellenwertfunktion gibt nur den letzten Wert zurück
  4. Beim Verketten von varchar (max) / nvarchar (max) -Werten aus einer Tabellenvariablen können falsche Ergebnisse zurückgegeben werden, wenn nach einer Nicht-Primärschlüsselspalte gefiltert und sortiert wird

Beispielantwort von Microsoft:

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.

Die Antwort verweist auch auf KB 287515:

PRB: Ausführungsplan und Ergebnisse aggregierter Verkettungsabfragen hängen vom Speicherort des Ausdrucks ab

Die Lösung ist die Verwendung FOR XML PATH(der zweite Ansatz in Aarons Antwort), wenn die Reihenfolge der Verkettung wichtig ist und Sie natürlich sicher sein möchten, alle Werte einzuschließen. Siehe auch:

nvarchar concatenation / index / nvarchar (max) unerklärliches Verhalten beim Stapelüberlauf

ebol2000
quelle