如何在m到n关系表中将行转换为新列?

时间:2019-10-01 11:19:12

标签: sql tsql many-to-many multiple-columns

我需要一些问题的帮助。

我正在使用SQL在Microsoft数据库上工作,并且有一个与此表类似的表:

|*Name*|*Permissions*|
|App1  |Permission 1 |
|App1  |Permission 2 |
|App1  |Permission 3 |
|App2  |Permission 1 |
|App2  |Permission 2 |
|App2  |Permission 3 |

因此这是一个m对n的关系,因为有多个权限分配给一个应用程序,但也有多个应用程序使用相同的权限。

我已经尝试过使用诸如以下这样的方法将表与其自身连接起来:

select distinct apps1.name, apps2.permissions from Apps apps1
join Apps apps2
where apps2.name IN
(SELECT distinct name from Apps);

但是它并没有给我我想要的结果。

我想要一个看起来像这样的结果

|*Name*|*Permission1*|*Permission2*|*Permission3*|
|App1  |Permission 1 |Permission 2 |Permission 3 |
|App2  |Permission 1 |Permission 2 |Permission 3 |

应用程序可能会获得不同数量的权限,但是如果是这样,则可以将这些空列填充为NULL值。

希望您能帮助我。 如果需要更多信息,请给我写信。由于我是StackOverflow的新手,所以我不是写下问题的专业人士。

4 个答案:

答案 0 :(得分:1)

您可以为此使用PIVOT运算符。下面是一个示例。

CREATE TABLE #Permission
(
 Name       VARCHAR(10)
,Permission VARCHAR(25)
)

INSERT INTO #Permission (Name, Permission) VALUES
('App1', 'Permission 1')
,('App1', 'Permission 2')
,('App1', 'Permission 3')
,('App2', 'Permission 1')
,('App2', 'Permission 2')
,('App2', 'Permission 3')
,('App3', 'Permission 2');


SELECT * 
FROM
    (
    SELECT Name, Permission FROM #Permission
    ) AS A
PIVOT
    (
    MAX(Permission)
    FOR Permission IN ([Permission 1], [Permission 2], [Permission 3])
    ) AS PivotTable

DROP TABLE #Permission;

如果您具有 n 个权限,则可能必须使用dynamic pivoting

答案 1 :(得分:0)

如果您拥有最多的权限(例如三个),则可以使用条件聚合(或pivot):

select a.name,
       max(case when seqnum = 1 then a.permission end) as permission_1,
       max(case when seqnum = 2 then a.permission end) as permission_2,
       max(case when seqnum = 3 then a.permission end) as permission_3
from (select a.*,
             row_number() over (partition by a.name order by a.name) as seqnum
      from apps a
     ) a
group by a.name;

如果您不知道要返回多少列,则需要使用动态SQL,查询会更加复杂。

答案 2 :(得分:0)

您可以使用PIVOT进行此类查询:

一个模拟场景的

DECLARE @mockupTable TABLE([Name] VARCHAR(100),[Permissions] VARCHAR(100));
INSERT INTO @mockupTable VALUES
 ('App1','Permission 1')
,('App1','Permission 2')
,('App1','Permission 3')
,('App2','Permission 1')
,('App2','Permission 2')
,('App2','Permission 3');

-查询

SELECT p.*
FROM
(
    SELECT t.*
          ,CONCAT('Permission',ROW_NUMBER() OVER(PARTITION BY t.[Name] ORDER BY t.[Permissions])) AS ColumnName
    FROM @mockupTable t
) tbl
PIVOT
(
    MAX(tbl.[Permissions]) 
    FOR tbl.ColumnName 
    IN(Permission1,Permission2,Permission3,Permission4 /*add as many as you need*/)
) p;

简而言之:

使用CONCATROW_NUMBER,我们可以创建一个值,以后将其用作数据透视表的列名。

答案 3 :(得分:0)

您也可以尝试使用CTE

;with cte as (
SELECT *
        ,ROW_NUMBER() over(partition by Name order by Permissions ) rn
FROM YourTable
) 

select      Name
            ,MAX(case   when rn=1 then Permissions   end) Permission1
            ,MAX(case   WHEN rn=2 then Permissions  end) Permission2
            ,MAX(case   WHEN rn=3 then Permissions  end) Permission3
FROM cte 
group by Name