动态创建列sql

Posted

技术标签:

【中文标题】动态创建列sql【英文标题】:Dynamically create columns sql 【发布时间】:2012-09-20 12:31:45 【问题描述】:

我有一张客户表

Customer ID        Name         
   1               John        
   2               Lewis        
   3               Mary         

我有另一张表 CustomerRewards

 TypeID           Description
   1                Bronze
   2                Silver
   3                Gold
   4               Platinum
   5               AnotherOne

决赛桌

 RewardID          TypeID          CustomerID
    1                1                 1
    2                1                 1
    3                2                 1
    4                2                 2

customerTypes 表是动态的,其中许多类型可以添加和删除。基本上我想要的只是动态生成的列和每个列的计数,比如

CustomerName        Bronze        Silver     Gold      Platinum     AnotherOne    total
   John               2             1         0           0             0           3
   Lewis              0             1         0           0             0           1
 Grand TOTAL          2             2         0           0             0           4

问题就像我说的那样,类型是动态的,客户是动态的,所以我需要根据系统中的类型动态列

我已经在 DataGridView 中标记了 c#,因为我需要它

提前致谢

【问题讨论】:

这看起来像一个支点。我已经做了一些,但我无法从记忆中输入一个。也许是通过在 SQL 端旋转数据创建的数据库中的视图? 如果您需要在 SQL 中执行此操作,您可能需要动态数据透视。参见,例如,simple-talk.com/blogs/2007/09/14/… 但这并不是一个真正干净的解决方案。如果您可以等待将数据导入 C#,您可以使用 LINQ 来执行此操作。 这些看起来确实很复杂!感谢 cmets,我会研究 Pivot,但如果有人有简单的解决方案,请随意 SQL Server dynamic PIVOT query?的可能重复 【参考方案1】:

您需要为此使用PIVOT 函数。如果您有已知数量的列,那么您可以对这些值进行硬编码:

select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
from
(
  select c.name,
    cr.description,
    r.typeid
  from customers c
  left join rewards r
    on c.id = r.customerid
  left join customerrewards cr
    on r.typeid = cr.typeid
) x
pivot
(
  count(typeid)
  for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
) p;

见SQL Fiddle with Demo。

现在如果你有未知数量的列,那么你可以使用动态 SQL 来PIVOT

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

select @cols = STUFF((SELECT ',' + QUOTENAME(description) 
                    from customerrewards
                    group by description, typeid
                    order by typeid
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT name,' + @cols + ' from 
             (
                select c.name,
                  cr.description,
                  r.typeid
                from customers c
                left join rewards r
                  on c.id = r.customerid
                left join customerrewards cr
                  on r.typeid = cr.typeid
            ) x
            pivot 
            (
                count(typeid)
                for description in (' + @cols + ')
            ) p '

execute(@query)

见SQL Fiddle With Demo

如果您需要包含Total 列,则可以使用ROLLUP (Static Version Demo):

select name, sum([Bronze]) Bronze, sum([Silver]) Silver, 
  sum([Gold]) Gold, sum([Platinum]) Platinum, sum([AnotherOne]) AnotherOne
from 
(
  select name, [Bronze], [Silver], [Gold], [Platinum], [AnotherOne]
  from
  (
    select c.name,
      cr.description,
      r.typeid
    from customers c
    left join rewards r
      on c.id = r.customerid
    left join customerrewards cr
      on r.typeid = cr.typeid
  ) x
  pivot
  (
    count(typeid)
    for description in ([Bronze], [Silver], [Gold], [Platinum], [AnotherOne])
  ) p
) x
group by name with rollup

动态版(Demo):

DECLARE @cols AS NVARCHAR(MAX),
    @colsRollup AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT ',' + QUOTENAME(description) 
                    from customerrewards
                    group by description, typeid
                    order by typeid
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @colsRollup 
      = STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ QUOTENAME(description)
                    from customerrewards
                    group by description, typeid
                    order by typeid
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query 
          = 'SELECT name, '+ @colsRollup + '
             FROM
             (
                SELECT name,' + @cols + ' from 
                 (
                    select c.name,
                      cr.description,
                      r.typeid
                    from customers c
                    left join rewards r
                      on c.id = r.customerid
                    left join customerrewards cr
                      on r.typeid = cr.typeid
                ) x
                pivot 
                (
                    count(typeid)
                    for description in (' + @cols + ')
                ) p 
              ) x1
              GROUP BY name with ROLLUP'

execute(@query)

【讨论】:

你有一个 SQL 注入缺陷/可用性错误: = STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ description 应该是: = STUFF((SELECT ', Sum(' + QUOTENAME(description) + ') as '+ QUOTENAME(description) 否则,这是一个很棒的帖子! @shellster 是的,你是对的。我更新了我的代码。感谢您了解这一点。

以上是关于动态创建列sql的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server:从链接到另一个表的 ID 列动态创建列

sql动态创建多列

如何创建基于当前日期/月份动态更改列顺序的 sql 表?

通过存储过程创建具有动态列的视图

如何在 oracle 中使用动态 sql 创建记录类型?

创建一个表,其中动态 PL/SQL 中的“日期条件”