CTE - это просто временно созданная связь (обе таблицы и представления являются отношениями), которая существует только для «жизни» текущего запроса.
Я играл с именами CTE и полемимена.Мне действительно не нравится многократное использование имен полей, таких как id ;Я склонен думать, что это сбивает с толку.И поскольку единственное использование для names.id - это ORDER BY в первом операторе ROW_NUMBER (), я не буду использовать его в дальнейшем.
WITH namesNumbered as (
select myId, Name,
ROW_NUMBER() OVER (
PARTITION BY myId
ORDER BY id
) as nameNum
FROM names
)
, namesJoined(myId, Name, nameCount) as (
SELECT myId,
Cast(Name AS VARCHAR(225)),
1
FROM namesNumbered nn1
WHERE nameNum = 1
UNION ALL
SELECT nn2.myId,
Cast(
Rtrim(nc.Name) + ',' + nn2.Name
AS VARCHAR(225)
),
nn.nameNum
FROM namesJoined nj
INNER JOIN namesNumbered nn2 ON nn2.myId = nj.myId
and nn2.nameNum = nj.nameCount + 1
)
SELECT myId, Name
FROM (
SELECT myID, Name,
ROW_NUMBER() OVER (
PARTITION BY myId
ORDER BY nameCount DESC
) AS finalSort
FROM namesJoined
) AS tmp
WHERE finalSort = 1
Первый CTE, namesNumbered , возвращает два поля, которые нас интересуют, и значение сортировки;мы не можем просто использовать names.id для этого, потому что нам нужно, чтобы каждое значение myId имело значения 1, 2, .... names.id будет иметь 1, 2 ... для myId = 1, но будет иметь более высокое начальное значение для последующих значений myId .
Второй CTE, namesJoined , должен иметь имена полей, указанные в сигнатуре CTE, поскольку он будет рекурсивным.Базовый случай (часть перед UNION ALL) дает нам записи, где nameNum = 1. Мы должны CAST () поле Name , поскольку оно будет увеличиваться при последующих проходах;нам нужно убедиться, что мы CAST () достаточно велики для обработки любого из выходных данных;мы всегда можем TRIM () позже, если это необходимо.Нам не нужно указывать псевдонимы для полей, потому что подпись CTE обеспечивает их.Рекурсивный регистр (после UNION ALL) объединяет текущий CTE с предыдущим, гарантируя, что последующие проходы будут использовать все более высокие значения nameNum .Нам нужно TRIM () для предыдущих итераций Name , затем добавить запятую и новое Name .Результатом будет, неявно, CAST () с более широким полем.
В конечном запросе будут получены только те поля, которые нам нужны ( myId , Name ) ив подзапросе многократно сортирует записи так, чтобы наибольшее значение namesJoined.nameCount получило 1 как значение finalSort .Затем мы сообщаем предложению WHERE, что нужно предоставить нам только одну эту запись (для каждого значения myId ).
Да, я присвоил псевдониму подзапрос как tmp , то естьпримерно настолько общий, насколько вы можете получить.Большинство механизмов SQL требуют, чтобы вы указали псевдониму подзапроса, даже если это единственное отношение, видимое в этот момент.