【问题标题】:SQL Query & unpivot SyntaxSQL 查询 & unpivot 语法
【发布时间】:2014-03-05 18:03:51
【问题描述】:

我有一个权限系统,它定义了用户对某些项目的访问权限。

我需要一个报告页面来显示成员及其对项目的访问权限。

表格需要是这样的:

+---------+--------+--------+------+--------+
| Members | Item 1 | Item 2 | .... | Item N |
+---------+--------+--------+------+--------+
| Member1 |   x    |    +   | .... |   +    |
+---------+--------+--------+------+--------+
| Member2 |   +    |    x   | .... |   +    |
+---------+--------+--------+------+--------+

我有一个 3 表连接查询,用于列出成员和他们可以访问的项目,但我无法将其转换为数据透视语法。

Select members.id memberID, members.name memberName, items.name as item
From members
Left Join member_permission On memberID = members.id
Left Join items On items.id = member_permission.itemID

这是我的无效查询:

Select memberName, itemName
From (
    select members.id, members.name, item.name as itemName
    from members
    Left Join member_permission On memberID = members.id
    Left Join item On item.id = member_permission.itemID
) p
Unpivot
(itemName For name IN (item.name)) as unp

以及我收到的错误:

UNPIVOT 运算符中指定的列名“name”与 UNPIVOT 参数中的现有列名冲突。

我创建了一个例子来玩Sqlfiddle

【问题讨论】:

    标签: sql sql-server pivot unpivot


    【解决方案1】:

    您不需要为此查询使用 UNPIVOT。 UNPIVOT 用于将多列转换为多行。您只需应用 PIVOT 功能即可将您的项目变成列。

    我首先建议使用像 row_number() 这样的窗口函数来创建新的列标题,然后应用 PIVOT 函数:

    select id, name, Item1, Item2, Item3
    from
    (
      select members.id, members.name, items.name as item,
        'item'+
          cast(row_number() over(partition by members.id 
                                 order by members.id) as varchar(10)) col
      from members
      Left Join member_permission 
        On memberID = members.id
      Left Join items 
        On items.id = member_permission.itemID
    ) d
    pivot
    (
      max(item)
      for col in (Item1, Item2, Item3)
    ) piv;
    

    SQL Fiddle with Demo。然后,如果您有未知数量的值,您的查询将需要使用动态 SQL:

    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)
    
    select @cols = STUFF((SELECT ',' + QUOTENAME('item'+cast(seq as varchar(10))) 
                        from
                        (
                          select row_number() over(partition by memberid
                                                   order by memberid) seq
                          from member_permission
                        ) d
                        group by seq
                        order by seq
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    set @query = N'SELECT id, name, ' + @cols + N' 
                from 
                (
                  select members.id, members.name, items.name as item,
                    ''item''+
                      cast(row_number() over(partition by members.id 
                                             order by members.id) as varchar(10)) col
                  from members
                  Left Join member_permission 
                    On memberID = members.id
                  Left Join items 
                    On items.id = member_permission.itemID
                ) x
                pivot 
                (
                    max(item)
                    for col in (' + @cols + N')
                ) p '
    
    execute sp_executesql @query;
    

    SQL Fiddle with Demo

    【讨论】:

    • 感谢您的回答,最后一个细节;如何使用项目名称作为列名?即:“蓝色项目,红色项目”而不是“项目 1,项目 2”
    • @dvdnhm 您仍然可以使用相同的过程,但不要使用 rownumber() 生成列列表,而是使用列中的值。
    猜你喜欢
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-17
    • 1970-01-01
    相关资源
    最近更新 更多