Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Manière optimale de concaténer/agréger des chaînes

SOLUTION

La définition de optimal peut varier, mais voici comment concaténer des chaînes de différentes lignes à l'aide de Transact SQL standard, ce qui devrait fonctionner correctement dans Azure.

;WITH Partitioned AS
(
    SELECT 
        ID,
        Name,
        ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Name) AS NameNumber,
        COUNT(*) OVER (PARTITION BY ID) AS NameCount
    FROM dbo.SourceTable
),
Concatenated AS
(
    SELECT 
        ID, 
        CAST(Name AS nvarchar) AS FullName, 
        Name, 
        NameNumber, 
        NameCount 
    FROM Partitioned 
    WHERE NameNumber = 1

    UNION ALL

    SELECT 
        P.ID, 
        CAST(C.FullName + ', ' + P.Name AS nvarchar), 
        P.Name, 
        P.NameNumber, 
        P.NameCount
    FROM Partitioned AS P
        INNER JOIN Concatenated AS C 
                ON P.ID = C.ID 
                AND P.NameNumber = C.NameNumber + 1
)
SELECT 
    ID,
    FullName
FROM Concatenated
WHERE NameNumber = NameCount

EXPLICATION

L'approche se résume en trois étapes :

  1. Numéroter les lignes en utilisant OVER et PARTITION en les regroupant et en les ordonnant selon les besoins de la concaténation. Le résultat est Partitioned CTE. Nous gardons le nombre de lignes dans chaque partition pour filtrer les résultats ultérieurement.

  2. Utilisation de CTE récursif (Concatenated ) parcourir les numéros de ligne (NameNumber colonne) en ajoutant Name valeurs à FullName colonne.

  3. Filtrez tous les résultats sauf ceux avec le NameNumber le plus élevé .

Veuillez garder à l'esprit que pour rendre cette requête prévisible, il faut définir les deux regroupements (par exemple, dans vos lignes de scénario avec le même ID sont concaténés) et le tri (j'ai supposé que vous triiez simplement la chaîne par ordre alphabétique avant la concaténation).

J'ai rapidement testé la solution sur SQL Server 2012 avec les données suivantes :

INSERT dbo.SourceTable (ID, Name)
VALUES 
(1, 'Matt'),
(1, 'Rocks'),
(2, 'Stylus'),
(3, 'Foo'),
(3, 'Bar'),
(3, 'Baz')

Le résultat de la requête :

ID          FullName
----------- ------------------------------
2           Stylus
3           Bar, Baz, Foo
1           Matt, Rocks