【问题标题】:Can SQL Server Pivot without knowing the resulting column names?SQL Server Pivot 可以在不知道结果列名的情况下进行吗?
【发布时间】:2011-01-11 07:41:44
【问题描述】:

我有一张如下所示的表格:

Month      Site          Val
2009-12    Microsoft      10
2009-11    Microsoft      12
2009-10    Microsoft      13
2009-12    Google         20
2009-11    Google         21
2009-10    Google         22

我想得到一个二维表,它为我提供每个站点月份的“Val”,例如:

Month      Microsoft      Google
2009-12        10           20
2009-11        12           21
2009-10        13           22

但问题是,我不知道“站点”中所有可能的值。如果出现新网站,我想在结果表中自动获取新列。

我看到的所有可以做到这一点的代码示例都要求我在查询文本中硬编码“Microsoft 和 Google”。
I saw one that didn't,但这基本上是通过列出站点并在fly(连接一个字符串),其中包含这些列名。

难道没有办法让 SQL Server 2008 做到这一点而无需像这样的 hack 吗?

注意:我需要能够将其作为从 ASP.Net 发送的查询运行,我不能执行存储过程或其他类似的事情。

谢谢!
丹尼尔

【问题讨论】:

  • 通过在运行时使用exec (@sql) 执行查询,应该可以实现您要查找的内容,请参阅here
  • 10 年了,不知道列名的情况下是否可以进行转置?

标签: sql sql-server-2008 pivot


【解决方案1】:

您链接到的示例使用动态 SQL。不幸的是,当事先不知道输出列时,SQL Server 中没有其他内置方法可以进行透视。

如果数据不是太大,最简单的方法可能是简单地从 ASP.NET 运行一个普通的行查询并在应用程序代码中执行您的数据透视。如果数据非常大,那么您必须在第一次查询可能的列值后动态生成 SQL。

请注意,您实际上并不需要编写生成动态 SQL 的 SQL 语句;您可以简单地在 ASP.NET 中生成 SQL,这很可能会容易得多。只是不要忘记在将不同的 Site 值放入生成的查询之前对其进行转义,并且不要忘记参数化 SQL 语句的任何部分,您通常会在没有枢轴的情况下进行参数化。

【讨论】:

    【解决方案2】:
    select 
        month,
        min(case site  when 'microsoft'then val end) microsoft,
        min(case site  when 'google'then val end) google
    from 
        withoutpivot
    group by 
        month
    
    select 
        main.month,
        m.val as microsoft,
        g.val as google
    from    
        withoutpivot main
    inner join 
        withoutpivot m on m.month = main.month
    inner join 
        withoutpivot g on g.month = main.month
    where   
        m.site = 'microsoft'
        and g.site = 'google'
    

    【讨论】:

      【解决方案3】:

      已经10多年了,同样的问题也来找我。

      有没有什么方法可以在不知道列名的情况下进行旋转?

      然后我搜索了一些东西并找到了以下解决方案。我们可以通过使用动态查询来实现这一点。我添加这个是为了帮助别人。

      CREATE TABLE TEMP 
      (
           [Month] varchar(50),
           [Site] varchar(50),
           Val int
      )
      
      INSERT INTO TEMP
      VALUES ('2009-12', 'Microsoft', 10),
             ('2009-11', 'Microsoft', 12),
             ('2009-10', 'Microsoft', 15),
             ('2009-12', 'Google', 20),
             ('2009-11', 'Google', 8),
             ('2009-10', 'Google', 11),
             ('2009-12', 'Facebook', 13),
             ('2009-11', 'Facebook', 12),
             ('2009-10', 'Facebook', 5)
      
      DECLARE @Columns as VARCHAR(MAX)
      
      SELECT @Columns = COALESCE(@Columns + ', ','') + QUOTENAME([Site])
      FROM
          (SELECT DISTINCT [Site] FROM TEMP) AS B
      ORDER BY B.[Site]
      
      DECLARE @SQL as VARCHAR(MAX)
      SET @SQL = 'SELECT Month, ' + @Columns + ' 
      FROM
      (
       select Month,[Site],Val from TEMP
      ) as PivotData
      PIVOT
      (
         Sum(Val)
         FOR [Site] IN (' + @Columns + ')
      ) AS PivotResult
      ORDER BY Month'
      
      
      EXEC(@SQL);
      

      如您所见,我将列值转换为字符串,然后动态地使用它来进行透视。

      结果如下:

      【讨论】:

        【解决方案4】:

        如果我们把 marc_s 的答案放到一个过程中,我们有这样的:

        create procedure spPivot (
            @DataSource varchar(max), 
            @Column1 varchar(100),
            @PivotColumn varchar(100),
            @AggregateColumn varchar(100),
            @AgregateFunction varchar(20),
            @Debug bit = 0) as
        
        declare @SQL varchar(max) = 
        'DECLARE @Columns as VARCHAR(MAX)
        
        SELECT @Columns = COALESCE(@Columns + '', '','''') + QUOTENAME({PivotColumn})
        FROM (SELECT DISTINCT {PivotColumn} FROM {DataSourceA} ds) c
        ORDER BY {PivotColumn}
        
        DECLARE @SQL as VARCHAR(MAX)
        SET @SQL = ''SELECT {Column1}, '' + @Columns + '' 
        FROM {DataSourceB} as PivotData
        PIVOT (
           {AgregateFunction}({AggregateColumn})
           FOR {PivotColumn} IN ('' + @Columns + '')
        ) AS PivotResult
        ORDER BY {Column1}''
        
        EXEC(@SQL)'
        
        if @DataSource like 'select %' begin
            set @SQL = replace(@SQL, '{DataSourceA}', '(' + @DataSource + ')')
            set @SQL = replace(@SQL, '{DataSourceB}', '(' + replace(@DataSource, '''', '''''') + ')')
        end else begin
            set @SQL = replace(@SQL, '{DataSourceA}', @DataSource)
            set @SQL = replace(@SQL, '{DataSourceB}', @DataSource)
        end
        set @SQL = replace(@SQL, '{Column1}', @Column1)
        set @SQL = replace(@SQL, '{PivotColumn}', @PivotColumn)
        set @SQL = replace(@SQL, '{AggregateColumn}', @AggregateColumn)
        set @SQL = replace(@SQL, '{AgregateFunction}', @AgregateFunction)
        
        if @Debug = 1
            print @SQL
        else
            exec(@SQL)
        

        及其用法示例:

        spPivot 
            'select ''Bucket'' Category, ''Large'' SubCategory, 1 Amount  union all
            select ''Bucket'' Category, ''Large'' SubCategory, 2 Amount  union all
            select ''Shovel'' Category, ''Large'' SubCategory, 4 Amount  union all
            select ''Shovel'' Category, ''Small'' SubCategory, 8 Amount',
            'Category', 'SubCategory', 'Amount', 'sum'
        

        该示例有效,但请注意,将 [temp] 表的名称发送给过程可能更有效,因为它在其中被查询了两次。所以使用 marc_s 的临时表,调用将是

        spPivot 'TEMP', '[Month]', 'Site', 'Val', 'SUM'
        

        还请注意,您有一个 @debug 参数,您可以使用该参数来找出您的调用未按预期工作的原因。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-12-23
          • 1970-01-01
          • 1970-01-01
          • 2018-08-20
          • 1970-01-01
          • 1970-01-01
          • 2013-02-01
          • 1970-01-01
          相关资源
          最近更新 更多