Конкатентные группы в SQL Server

Если у меня есть таблица вроде этого:

+------------+
| Id | Value |
+------------+
| 1  | 'A'   |
|------------|
| 1  | 'B'   |
|------------|
| 2  | 'C'   |
+------------+

Как я могу получить набор результатов следующим образом:

+------------+
| Id | Value |
+------------+
| 1  | 'AB'  |
|------------|
| 2  | 'C'   |
+------------+

Я знаю, что это действительно легко сделать в MySQL, используя GROUP_CONCAT, но мне нужно иметь возможность сделать это в MSSQL 2005

Спасибо

(Дубликат Как использовать GROUP BY для конкатенации строк в SQL Server?)

Ответ 1

Для чистого и эффективного решения вы можете создать пользовательскую агрегатную функцию, есть даже пример, который делает то, что вам нужно.
Затем вы можете использовать его, как и любую другую совокупную функцию (со стандартным планом запросов):

query plan

Ответ 2

Это сделает:

SELECT mt.ID,
       SUBSTRING((SELECT mt2.Value
                  FROM   MyTable AS mt2
                  WHERE  mt2.ID = mt.ID
                  ORDER BY mt2.VALUE
                  FOR XML PATH('')), 3, 2000) AS JoinedValue
FROM   MyTable AS mt

Ответ 4

Часто спросил здесь.

Наиболее эффективным способом является использование трюка FOR XML PATH.

Ответ 5

Это просто стало для меня одним из возможных решений. Я понятия не имею, что касается производительности, но я подумал, что это будет интересный способ решить проблему. Я тестировал, что он работает в простой ситуации (я не закодировал для учета NULL). Не стесняйтесь дать ему тест, чтобы убедиться, что он хорошо работает для вас.

В таблице, которую я использовал, был указан id (my_id). Это действительно может быть любой столбец, который уникален внутри группы (grp_id), поэтому он может быть столбцом даты или любым другим.

;WITH CTE AS (
    SELECT
        T1.my_id,
        T1.grp_id,
        CAST(T1.my_str AS VARCHAR) AS my_str
    FROM
        dbo.Test_Group_Concat T1
    WHERE NOT EXISTS (SELECT * FROM dbo.Test_Group_Concat T2 WHERE T2.grp_id = T1.grp_id AND T2.my_id < T1.my_id)
    UNION ALL
    SELECT
        T3.my_id,
        T3.grp_id,
        CAST(CTE.my_str + T3.my_str AS VARCHAR)
    FROM
        CTE
    INNER JOIN dbo.Test_Group_Concat T3 ON
        T3.grp_id = CTE.grp_id AND
        T3.my_id > CTE.my_id
    WHERE
        NOT EXISTS (SELECT * FROM dbo.Test_Group_Concat T4 WHERE
        T4.grp_id = CTE.grp_id AND
        T4.my_id > CTE.my_id AND
        T4.my_id < T3.my_id)
)
SELECT
    CTE.grp_id,
    CTE.my_str
FROM
    CTE
INNER JOIN (SELECT grp_id, MAX(my_id) AS my_id FROM CTE GROUP BY grp_id) SQ ON
    SQ.grp_id = CTE.grp_id AND
    SQ.my_id = CTE.my_id
ORDER BY
    CTE.grp_id