开发者

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:

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.

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 withou开发者_如何转开发t a hack like that?

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.

Thanks!

Daniel


The example you linked to uses dynamic SQL. Unfortunately, there is no other built-in method for pivoting in SQL Server when the output columns are not known in advance.

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. If the data is very large, then you'll have to generate the SQL dynamically after first querying for the possible column values.

Note that you don't actually need to write a SQL statement that generates dynamic SQL; you can simply generating the SQL in ASP.NET, and that will most likely be much easier. 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.


It's been more than 10 years, and the same problem came to me.

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:

Can SQL Server Pivot without knowing the resulting column names?


If we take the answer of marc_s and put it into a procedure, we have this:

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. So using marc_s' temp table, the call would be

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

Also note you have a @debug parameter that you can use to figure out why your call is not working as you expect.


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'
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜