Ссылка @Dems опубликована функционально идентична вашим требованиям.
Однако, если у вас нет доступа к функциям xml, вот рекурсивная версия CTE:
WITH Indexed(id, value, index)
as (SELECT id, value, ROW_NUMBER() OVER(PARTITION BY id ORDER BY value)
FROM ValueTable),
Concatenated(id, index, concatenated)
as (SELECT id, index, value
FROM Indexed
WHERE index = 1
UNION ALL
SELECT a.id, a.index + 1, a.concatenated || ', ' || b.value
FROM Concatenated as a
JOIN Indexed as b
ON b.id = a.id
AND b.index = a.index + 1)
SELECT a.id, a.value
FROM Concatenated as a
EXCEPTION JOIN Indexed as b
ON b.id = a.id
AND b.index > a.index
Если у вас есть уникальный индекс в исходной таблице, Indexed
CTE можно заменить ссылкой на исходную таблицу.