I have a permission system that defines users access to certain items.

I need a report page to show members and their access to the items.

The table needs to be like this:

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

I have a 3 table joined query for listing the members and the items they can access but I could not convert it into pivot syntax.

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

This is the not working query I have:

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

and the error I receive:

The column name "name" specified in the UNPIVOT operator conflicts with the existing column name in the UNPIVOT argument.

I created an example to play with Sqlfiddle

有帮助吗?

解决方案

You don't need to use UNPIVOT for this query. UNPIVOT is used to convert multiple columns into multiple rows. You only need to apply the PIVOT function to turn your items into columns.

I would first suggest using a windowing function like row_number() to create your new column headers, then apply the PIVOT function:

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;

See SQL Fiddle with Demo. Then if you have an unknown number of values your query would need to use dynamic 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;

See SQL Fiddle with Demo

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top