简体   繁体   English

可以在不知道结果列名的情况下进行 SQL Server Pivot 吗?

[英]Can SQL Server Pivot without knowing the resulting column names?

I have a table that looks like this:我有一个看起来像这样的表:

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

And I want to get a 2-dimension table that gives me the "Val" for each site's month, like:我想得到一个二维表,它为我提供每个站点月份的“Val”,例如:

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

But the catch is, I don't know all the possible values that can be in "Site".但问题是,我不知道“站点”中所有可能的值。 If a new site appears, I want to automatically get a new column in my resulting table.如果出现新站点,我希望在结果表中自动获取一个新列。

All the code samples I saw that could do this required me to hardcode "Microsoft and Google" in the query text.我看到的所有可以执行此操作的代码示例都要求我在查询文本中对“Microsoft 和 Google”进行硬编码。
I saw one that didn't , but it was basically faking it by listing the Sites and generating a query on the fly (concatting a string) that had those column names in it. 我看到了一个没有的,但它基本上是通过列出站点并动态生成一个查询(连接一个字符串)来伪造它,其中包含这些列名称。

Isn't there a way to get SQL Server 2008 to do this without a hack like that?有没有办法让 SQL Server 2008 在没有这样的黑客的情况下做到这一点?

NOTE: I need to be able to run this as a query that I send from ASP.Net, I can't do stored procedures or other stuff like that.注意:我需要能够将此作为从 ASP.Net 发送的查询运行,我不能执行存储过程或其他类似的操作。

Thanks!谢谢!
Daniel丹尼尔

The example you linked to uses dynamic SQL.您链接到的示例使用动态 SQL。 Unfortunately, there is no other built-in method for pivoting in SQL Server when the output columns are not known in advance.不幸的是,当事先不知道输出列时,没有其他内置方法可用于在 SQL Server 中进行透视。

If the data is not too large, it's probably easiest to simply run a normal row query from ASP.NET and perform your pivot in the application code.如果数据不是太大,从 ASP.NET 运行普通行查询并在应用程序代码中执行数据透视可能是最简单的。 If the data is very large, then you'll have to generate the SQL dynamically after first querying for the possible column values.如果数据非常大,则必须在首先查询可能的列值后动态生成 SQL。

Note that you don't actually need to write a SQL statement that generates dynamic SQL;请注意,您实际上并不需要编写生成动态 SQL 的 SQL 语句; you can simply generating the SQL in ASP.NET, and that will most likely be much easier.您可以简单地在 ASP.NET 中生成 SQL,这很可能会容易得多。 Just don't forget to escape the distinct Site values before chucking them in a generated query, and don't forget to parameterize whatever parts of the SQL statement that you normally would without the pivot.只是不要忘记在将它们放入生成的查询中之前转义不同的Site值,并且不要忘记参数化 SQL 语句的任何部分,您通常会在没有枢轴的情况下进行参数化。

It's been more than 10 years, and the same problem came to me.已经10多年了,同样的问题出现在我身上。

Is there any way to pivot without knowing column names?有没有办法在不知道列名的情况下进行透视?

Then I searched something and found the below solution.然后我搜索了一些东西并找到了以下解决方案。 We can achieve this by using dynamic query.我们可以通过使用动态查询来实现这一点。 I am adding this so it will help someone.我正在添加这个,所以它会帮助某人。

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);

As you can see I took the column values into a string and then dynamically use that to pivot.如您所见,我将列值放入一个字符串中,然后动态地使用它进行透视。

Here is the result:结果如下:

在此处输入图片说明

If we take the answer of marc_s and put it into a procedure, we have this:如果我们把 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)

And an example of its usage:以及它的用法示例:

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'

The example works, but note that it's probably more efficient to send the procedure the name of a [temp] table because it's queried twice within.该示例有效,但请注意,将 [temp] 表的名称发送给过程可能更有效,因为它在其中被查询了两次。
Also note you have a @debug parameter that you can use to figure out why your call is not working as you expect.另请注意,您有一个 @debug 参数,您可以使用它来找出您的调用未按预期工作的原因。

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'

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM