Sql server 将多对多关系转换为单行

Sql server 将多对多关系转换为单行,sql-server,tsql,Sql Server,Tsql,我有下列表格 用户 Id FirstName LastName 样本数据 1,'Peter','Smith' 2,'John','Como' 手机 Id UserId PhoneTypeId Phone ContactName 样本数据 1,1,4,'555-555-5551','Peter' 2,1,4,'555-555-5552','Paul' 3,1,4,'555-555-5553','Nancy' 4,1,4,'555-555-5554','Hellen' 1 Home 2 Wo

我有下列表格

用户

Id
FirstName
LastName
样本数据

1,'Peter','Smith'
2,'John','Como'
手机

Id
UserId
PhoneTypeId
Phone
ContactName
样本数据

1,1,4,'555-555-5551','Peter'
2,1,4,'555-555-5552','Paul'
3,1,4,'555-555-5553','Nancy'
4,1,4,'555-555-5554','Hellen'
1 Home
2 Work
3 Cell
4 Emergency
电话类型

Id
Type
用样本数据

1,1,4,'555-555-5551','Peter'
2,1,4,'555-555-5552','Paul'
3,1,4,'555-555-5553','Nancy'
4,1,4,'555-555-5554','Hellen'
1 Home
2 Work
3 Cell
4 Emergency
我必须创建以下结果

UserId, UserFirstName, UserLastName, FirstEmergencyContactName, FirstEmergencyContactPhone, SecondEmergencyContactName, SecondEmergencyContactPhone, ThirdEmergencyContactName, ThirdEmergencyContactPhone, FourthEmergencyContactName, FourthEmergencyContactPhone, FifthEmergencyContactName, FifthEmergencyContactPhone

如何为每个有紧急联系人的用户创建一行?一些用户可能有一个紧急联系人,而其他用户可能有很多,但我只需要五个联系人。

这称为表透视。由于您需要的结果不超过5个,因此可以将
条件聚合
行数
一起使用:

select id, firstname, lastname,
       max(case when rn = 1 then contactname end) emergency_contact1,
       max(case when rn = 1 then phone end) emergency_phone1,
       max(case when rn = 2 then contactname end) emergency_contact2,
       max(case when rn = 2 then phone end) emergency_phone2,
       ...
from (
    select u.id, u.firstname, u.lastname, p.phone, p.contactname, 
           row_number() over (partition by u.id order by p.phonetypeid) rn
    from users u 
       join phones p on u.id = p.userid
) t
group by id, firstname, lastname

这称为表数据透视。由于您需要的结果不超过5个,因此可以将
条件聚合
行数
一起使用:

select id, firstname, lastname,
       max(case when rn = 1 then contactname end) emergency_contact1,
       max(case when rn = 1 then phone end) emergency_phone1,
       max(case when rn = 2 then contactname end) emergency_contact2,
       max(case when rn = 2 then phone end) emergency_phone2,
       ...
from (
    select u.id, u.firstname, u.lastname, p.phone, p.contactname, 
           row_number() over (partition by u.id order by p.phonetypeid) rn
    from users u 
       join phones p on u.id = p.userid
) t
group by id, firstname, lastname

您还可以使用数据透视,而无需动态SQL和硬编码,因为您只需要5个联系人/电话。示例如下:

;WITH cte AS (
SELECT  p.UserId, 
        FirstName, 
        LastName,
        CAST(ContactName as nvarchar(100)) as ContactName,
        CAST(Phone as nvarchar(100)) as ContactPhone,
        CAST(ROW_NUMBER() OVER (PARTITION BY p.UserId ORDER BY pt.Id) as nvarchar(100)) as RN
FROM Users u
INNER JOIN Phones p
    ON p.UserId = u.Id
INNER JOIN PhoneTypes pt
    ON pt.Id = p.PhoneTypeId
WHERE pt.Id = 4
)

SELECT *
FROM (
    SELECT  UserId, 
            FirstName, 
            LastName,
            [Columns]+RN as [Columns],
            [Values]
    FROM cte
    UNPIVOT (
        [Values] FOR [Columns] IN (ContactName, ContactPhone)
    ) as unp
) as t
PIVOT (
    MAX([Values]) FOR [Columns] IN (ContactName1,ContactPhone1,ContactName2,ContactPhone2,ContactName3,ContactPhone3,
ContactName4,ContactPhone4,ContactName5,ContactPhone5)
) as pvt
输出:

UserId  FirstName   LastName    ContactName1    ContactPhone1   ContactName2    ContactPhone2   ContactName3    ContactPhone3   ContactName4    ContactPhone4   ContactName5    ContactPhone5
1       Peter       Smith       Peter           555-555-5551    Paul            555-555-5552    Nancy           555-555-5553    Hellen          555-555-5554    NULL            NULL
2       John        Cono        Harry           555-555-5555    William         555-555-5556    John            555-555-5557    NULL            NULL            NULL            NULL    

我添加了一些联系人。

您还可以使用数据透视,而无需动态SQL和硬编码,因为您只需要5个联系人/电话。示例如下:

;WITH cte AS (
SELECT  p.UserId, 
        FirstName, 
        LastName,
        CAST(ContactName as nvarchar(100)) as ContactName,
        CAST(Phone as nvarchar(100)) as ContactPhone,
        CAST(ROW_NUMBER() OVER (PARTITION BY p.UserId ORDER BY pt.Id) as nvarchar(100)) as RN
FROM Users u
INNER JOIN Phones p
    ON p.UserId = u.Id
INNER JOIN PhoneTypes pt
    ON pt.Id = p.PhoneTypeId
WHERE pt.Id = 4
)

SELECT *
FROM (
    SELECT  UserId, 
            FirstName, 
            LastName,
            [Columns]+RN as [Columns],
            [Values]
    FROM cte
    UNPIVOT (
        [Values] FOR [Columns] IN (ContactName, ContactPhone)
    ) as unp
) as t
PIVOT (
    MAX([Values]) FOR [Columns] IN (ContactName1,ContactPhone1,ContactName2,ContactPhone2,ContactName3,ContactPhone3,
ContactName4,ContactPhone4,ContactName5,ContactPhone5)
) as pvt
输出:

UserId  FirstName   LastName    ContactName1    ContactPhone1   ContactName2    ContactPhone2   ContactName3    ContactPhone3   ContactName4    ContactPhone4   ContactName5    ContactPhone5
1       Peter       Smith       Peter           555-555-5551    Paul            555-555-5552    Nancy           555-555-5553    Hellen          555-555-5554    NULL            NULL
2       John        Cono        Harry           555-555-5555    William         555-555-5556    John            555-555-5557    NULL            NULL            NULL            NULL    
我添加了一些联系人