web-dev-qa-db-fra.com

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

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 obtenir 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

29
user2732180

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 fonction STRING_AGG() :

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];
47
Aaron Bertrand

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

Sachez qu'un ORDER BY peut ne fonctionner qu'en incluant le dernier élément de 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 la commande 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 indéfini.

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

PRB: plan d'exécution et résultats des requêtes de concaténation d'agrégats dépendent de l'emplacement de l'expression

La solution consiste à 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:

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

7
ebol2000