Comment utiliser COALESCE avec plusieurs lignes et sans virgule précédente?

27

J'essaie d'atteindre les objectifs suivants:

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

Malheureusement, je reçois ", Los Angeles, San Francisco, Sacramento, Jacksonville, Miami"

Je peux atteindre les résultats souhaités en utilisant la fonction STUFF, mais je me demandais s'il y avait une façon plus propre de le faire en utilisant COALESCE?

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;

Merci

user2732180
la source

Réponses:

45

Cela pourrait être l'approche plus propre que vous recherchez. Fondamentalement, vérifiez si la variable a encore été initialisée. Si ce n'est pas le cas, définissez-le sur la chaîne vide et ajoutez la première ville (pas de virgule principale). Si c'est le cas, ajoutez une virgule, puis ajoutez la ville.

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

Bien sûr, cela ne fonctionne que pour remplir une variable par état. Si vous tirez la liste pour chaque état un par un, il existe une meilleure solution en une seule fois:

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];

Résultats:

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

Pour commander par nom de ville dans chaque état:

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];

Dans Azure SQL Database ou SQL Server 2017+, vous pouvez utiliser la nouvelle STRING_AGG()fonction :

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

Et classés par nom de ville:

SELECT [state], cities = STRING_AGG(city, N', ') 
                         WITHIN GROUP (ORDER BY city)
  FROM dbo.tbl
  GROUP BY [state]
  ORDER BY [state];
Aaron Bertrand
la source
Merci Aaron. Ma solution actuelle est presque identique à la vôtre, sauf que j'utilise DISTINCT au lieu de GROUP BY.
user2732180
2
@ user2732180 Vous devez utiliser un GROUP BY car il est plus susceptible d'effectuer la concaténation une fois par état. Avec DISTINCT, il appliquera la même concaténation pour chaque instance de Californie, par exemple, et ne supprimera ensuite que tout le travail effectué pour générer ces doublons.
Aaron Bertrand
6

Juste pour ajouter à la réponse d' Aaron ci-dessus ...

Sachez qu'un an ORDER BYpeut casser uniquement en incluant le dernier élément dans votre requête. Dans mon cas, je ne faisais pas de regroupement, donc je ne sais pas si cela fait une différence. J'utilise SQL 2014. Dans mon cas, j'ai quelque chose comme value1, value2, value3 ... mais mon résultat dans la variable n'était que value3.


Aaron a commenté:

Cela a été signalé au moins quatre fois sur Connect:

  1. Dans la concaténation variable et l'ordre par les résultats des filtres (comme où la condition)
  2. (n) la construction de varchar à partir de ResultSet échoue lorsque ORDER BY est ajouté
  3. L'affectation d'une variable locale à partir d'un SELECT ordonné avec CROSS APPLY et une fonction table renvoie uniquement la dernière valeur
  4. Lors de la concaténation des valeurs varchar (max) / nvarchar (max) à partir d'une variable de table, des résultats incorrects peuvent être renvoyés si le filtrage et le classement par une colonne de clé non primaire

Exemple de réponse de Microsoft:

Le comportement que vous voyez est de par leur conception. L'utilisation d'opérations d'affectation (concaténation dans cet exemple) dans les requêtes avec la clause ORDER BY a un comportement non défini.

La réponse fait également référence à l'article KB 287515:

Problème: plan d'exécution et les résultats des requêtes de concaténation agrégée dépendent de l'emplacement de l'expression

La solution est d'utiliser FOR XML PATH(la deuxième approche dans la réponse d'Aaron) si l'ordre de concaténation est important et, bien sûr, si vous voulez être sûr d'inclure toutes les valeurs. Regarde aussi:

nvarchar concaténation / index / nvarchar (max) comportement inexplicable sur Stack Overflow

ebol2000
la source