Сложный T-SQL Query.Объединить несколько значений из столбца между основными / дочерними таблицами - PullRequest
3 голосов
/ 12 июля 2011

У меня есть три таблицы следующим образом:

MasterTable

+----------+-------------+
| MasterId | MasterName  |
+----------+-------------+
| 1        | Master 1    |
| 2        | Master 2    |
| 3        | Master 3    |
| 4        | Master 4    |
+----------+-------------+

ChildrenTable

+----------+-------------+
| ChildId | ChildName    |
+----------+-------------+
| 1        | Child 1     |
| 2        | Child 2     |
| 3        | Child 3     |
| 4        | Child 4     |
+----------+-------------+

LinkTable

+----------+-----------------------+
| Id       | MasterId    | ChldId  |
+----------+-----------------------+
| 1        |  1          | 1       | 
| 2        |  2          | 1       | 
| 3        |  3          | 2       | 
| 4        |  4          | 3       | 
+----------+-----------------------+

Один ребенок может бытьсвязаны с несколькими мастерами и LinkTable содержит эту деталь.Я хочу, чтобы запрос выбрал следующее:

1, 'Child 1', 'Master 1, Master 2', '1,2'
2, 'Child 2', 'Master 2', '2'
3, 'Child 3', 'Master 3', '3'

Можно ли обойтись без циклов или вызова дополнительной функции, используя COALESCE, STUFF, рекурсивный CTE и т. Д.?

Ответы [ 2 ]

3 голосов
/ 12 июля 2011

Для объединения строк вы можете использовать этот метод: Как объединить все строки из определенного столбца для каждой группы

Данные испытаний:

declare @masterTable table(MasterId int identity, MasterName varchar(max))
insert @masterTable (MasterName) values('m1'), ('m2'), ('m3'), ('m4')

declare @childrenTable table(ChildId int identity, ChildName varchar(max))
insert @childrenTable (ChildName) values('c1'), ('c2'), ('c3'), ('c4')

declare @LinkTable table(MasterId1 int, MasterId2 int, ChildId int)
insert @LinkTable values(1,1,1), (2,2,1), (3,3,2), (4,4,3)

Запрос:

select t.*
from
(
    select c.ChildId, c.ChildName

        , STUFF((
            select ', ' + m.MasterName
            from
            (
                select l.MasterId1
                from @LinkTable l
                where l.ChildId = c.ChildId

                union

                select l.MasterId2
                from @LinkTable l
                where l.ChildId = c.ChildId
            )t
            join @masterTable m on m.MasterId = t.MasterId1
            for xml path(''), type
        ).value('.', 'varchar(max)'), 1, 2, '') [names]

        , STUFF((
            select ', ' + cast(t.MasterId1 as varchar(max))
            from
            (
                select l.MasterId1
                from @LinkTable l
                where l.ChildId = c.ChildId

                union

                select l.MasterId2
                from @LinkTable l
                where l.ChildId = c.ChildId
            )t
            for xml path(''), type
        ).value('.', 'varchar(max)'), 1, 2, '') [ids]
    from @childrenTable c
)t
where t.ids is not null

Выход:

----------- --- -------- ------
1           c1  m1, m2   1, 2
2           c2  m3       3
3           c3  m4       4
1 голос
/ 13 июля 2011

Хотя решение @ polishchuk работает, у меня была своя версия ниже:

SELECT  ChildId, ChildName, ISNULL(mastDetail.MasterIds,'')MasterIds, 
        ISNULL(mastDetail.MasterNames, '') MasterNames
FROM    ChildrenTable sub
OUTER APPLY
(
    SELECT 
    STUFF( 
            (SELECT  ',' + mast.MasterName
             FROM    MasterTable mast
             INNER JOIN LinkTable link ON (mast.MasterId = link.MasterId AND 
                   link.ChildId = child.ChildId)
             FOR XML PATH('')
            ), 1,1,''
        ) AS MasterNames,
    STUFF( 
            (SELECT  ',' + CAST(mast.MasterId AS VARCHAR)
             FROM    MasterTable mast
             INNER JOIN LinkTable link ON (mast.MasterId = link.MasterId AND 
             link.ChildId = child.ChildId)
             FOR XML PATH('')
            ), 1,1,''
        ) AS MasterIds
) AS mastDetail
...