SQL也许是Pivot?但我需要实际价值而不是数字

时间:2013-06-05 18:18:30

标签: sql sql-server tsql pivot pivot-table

我想将每个字段名称的“Lock”或“Editable”的所有列名称放入“Visible by”字段中。有数百列,所以我正在寻找一种自动化的方法。

EG:

Field Name                   visible by   Business Development   VC Panel          Admin
Certification_Complete__c                   Hidden                 Lock             Editable
Certification_Status__c                     Hidden                 Hidden           Editable    

要:

Field Name                  visible by     Business Development    VC Panel        Admin
Certification_Complete__c  **VC Panel,Admin**   Hidden             Lock            Editable
Certification_Status__c     **Admin**           Hidden             Hidden          Editable

非常感谢

1 个答案:

答案 0 :(得分:3)

您的表结构是获得此结果的问题的一部分。理想情况下,您应该考虑将表重组为更规范化的结构。

您可以将结构更改为类似于此的内容:

CREATE TABLE certs
(
  [cert_id] int, 
  [FieldName] varchar(25)
);

INSERT INTO certs ([cert_id], [FieldName])
VALUES (1, 'Certification_Complete__c'),
    (2, 'Certification_Status__c');

CREATE TABLE permissions
(
  [permission_id] int, 
  [permissionName] varchar(20)
);

INSERT INTO permissions ([permission_id], [permissionName])
VALUES (1, 'Business Development'),
    (2, 'VC Panel'),
    (3, 'Admin');

CREATE TABLE certs_permissions
(
  [cert_id] int, 
  [permission_id] int, 
  [permission_type] varchar(8)
);

INSERT INTO certs_permissions ([cert_id], [permission_id], [permission_type])
VALUES
    (1, 1, 'Hidden'),
    (1, 2, 'Lock'),
    (1, 3, 'Editable'),
    (2, 1, 'Hidden'),
    (2, 2, 'Hidden'),
    (2, 3, 'Editable');

然后,您可以通过连接表并使用PIVOT函数将行转换为列来获得结果。代码与此类似:

;with cte as
(
  select c.cert_id, c.fieldname, p.permissionName, 
    cp.permission_type
  from certs c
  inner join certs_permissions cp
    on c.cert_id = cp.cert_id
  inner join permissions p
    on cp.permission_id = p.permission_id
) 
select fieldname,
  visibleby, 
  [Business Development], 
  [VC Panel],
  [Admin]
from
(
  select c1.fieldname,
    STUFF(
         (SELECT ', ' + cast(t.permissionName as varchar(50))
          FROM cte t
          where c1.fieldname = t.fieldname
            and t.permission_type in ('Lock', 'Editable')
          FOR XML PATH (''))
          , 1, 1, '')  AS [visibleby],
    c1.permissionname,
    c1.permission_type
  from cte c1
) d
pivot
(
  max(permission_type)
  for permissionname in ([Business Development], [VC Panel],
                          [Admin])
) piv

SQL Fiddle with Demo。这给出了一个结果:

|                 FIELDNAME |        VISIBLEBY | BUSINESS DEVELOPMENT | VC PANEL |    ADMIN |
---------------------------------------------------------------------------------------------
|   Certification_Status__c |            Admin |               Hidden |   Hidden | Editable |
| Certification_Complete__c |  VC Panel, Admin |               Hidden |     Lock | Editable |

如果您不想对所有列值进行硬编码,可以更改上面的脚本以轻松实现动态SQL以生成要执行的查询字符串。

如果您无法修复数据库结构,那么我建议分两步完成。使用动态SQL来规范化当前的数据库结构,以便轻松获取逗号分隔的visible_by值列表。使用UNPIVOT函数时,可以将数据放入临时表中,该表可用于获取逗号分隔列表。代码与此类似:

DECLARE @colsUnpivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @colsUnpivot = STUFF((SELECT distinct ','+ quotename(c.column_name)
                   from INFORMATION_SCHEMA.COLUMNS as C
                   where (TABLE_NAME = 'yt') and
                      c.column_name not in ('Field Name', 'visible by')
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
  = 'select [Field Name], [visible by], colList, value
     into temp
     from yt
     unpivot
     (
       value
       for colList in ('+ @colsunpivot +')
     ) u'

exec(@query);

select distinct 
  t.[field name], 
  STUFF(
         (SELECT ', ' + cast(t2.colList as varchar(50))
          FROM temp t2
          where t.[Field Name] = t2.[Field Name]
            and t2.value in ('Lock', 'Editable')
          FOR XML PATH (''))
          , 1, 1, '')  AS [visible by],
  t.[Business Development],
  t.[VC Panel], 
  t.[Admin]
from yt t;

drop table temp;

请参阅SQL Fiddle with Demo