获取ROWS作为COLUMNS(SQL ServerdynamicPIVOT查询)

我正在使用MS SQL 2008 R2,有三个表格,具有以下模式:

表1:包含每个工作人员的轮class信息

CREATE TABLE workshift ( [ws_id] [bigint] NOT NULL, [start_date] [datetime] NOT NULL, [end_date] [datetime] NOT NULL, [worker_id] [bigint] NOT NULL ) INSERT INTO workshift VALUES (1, '2012-08-20 08:30:00', '2012-08-20 14:30:00', 1) INSERT INTO workshift VALUES (2, '2012-08-20 14:30:00', '2012-08-20 22:30:00', 2) 

表2:包含货币面值

 CREATE TABLE currency_denom ( [cd_id] [decimal](7, 2) NOT NULL, [name] [nchar](100) NOT NULL ) INSERT INTO currency_denom VALUES (1, '100.00') INSERT INTO currency_denom VALUES (2, '50.00') INSERT INTO currency_denom VALUES (3, '20.00') INSERT INTO currency_denom VALUES (4, '10.00') INSERT INTO currency_denom VALUES (5, '5.00') INSERT INTO currency_denom VALUES (6, '1.00') 

表3:包含工人在每次工作转移中所收到的每个面值的数量

 CREATE TABLE currency_by_workshift ( [cd_id] [decimal](7, 2) NOT NULL, [ws_id] [bigint] NOT NULL, [qty] [int] NOT NULL ) INSERT INTO currency_by_workshift VALUES (1, 1, 1) INSERT INTO currency_by_workshift VALUES (2, 1, 2) INSERT INTO currency_by_workshift VALUES (3, 1, 2) INSERT INTO currency_by_workshift VALUES (2, 2, 3) INSERT INTO currency_by_workshift VALUES (4, 2, 4) INSERT INTO currency_by_workshift VALUES (5, 2, 2) 

我需要获取列中的currency_by_workshift值而不是行,以及轮class值,即:

 workshift | workshift | workshift | 100.00 | 50.00 | 20.00 | 10.00 | 5.00 | 1.00 ws_id | start_date | end_date | | | | | | 1 | 2012-08-20 08:30:00 | 2012-08-20 14:30:00 | 1 | 2 | 2 | 0 | 0 | 0 2 | 2012-08-20 14:30:00 | 2012-08-20 22:30:00 | 0 | 2 | 0 | 4 | 2 | 0 

我无法使用个案来计算每个货币面额的数量,因为它们是可configuration的,如果添加了新的面额,则应该修改查询。 如果使用PIVOTfunction也一样,或者我错了?

我怎样才能得到这样的信息?

你正在做的是被称为PIVOT 。 有两种方法可以执行此操作,无论是使用静态透视还是dynamic透视。

Static Pivot(静态透视图) – 将硬编码行的值以转换为列的位置(请参阅SQL小提琴演示 ):

 select ws_id, start_date, end_date, IsNull([100.00], 0) [100.00], IsNull([50.00], 0) [50.00], IsNull([20.00], 0) [20.00], IsNull([10.00], 0) [10.00], IsNull([5.00], 0) [5.00], IsNull([1.00], 0) [1.00] from ( select ws.ws_id, ws.start_date, ws.end_date, cd.name, cbw.qty from workshift ws left join currency_by_workshift cbw on ws.ws_id = cbw.ws_id left join currency_denom cd on cbw.cd_id = cd.cd_id ) x pivot ( sum(qty) for name in ([100.00], [50.00], [20.00], [10.00], [5.00], [1.00]) ) p 

dynamic数据透视表是在运行时确定列的位置(请参阅SQL小提琴演示 ):

 DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX), @colsPivot AS NVARCHAR(MAX) select @colsPivot = STUFF((SELECT ', IsNull(' + QUOTENAME(rtrim(name)) +', 0) as ['+ rtrim(name)+']' from currency_denom GROUP BY name ORDER BY cast(name as decimal(10, 2)) desc FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'') select @cols = STUFF((SELECT distinct ', ' + QUOTENAME(name) from currency_denom FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)') ,1,1,'') set @query = 'SELECT ws_id, start_date, end_date,' + @colsPivot + ' from ( select ws.ws_id, ws.start_date, ws.end_date, cd.name, cbw.qty from workshift ws left join currency_by_workshift cbw on ws.ws_id = cbw.ws_id left join currency_denom cd on cbw.cd_id = cd.cd_id ) x pivot ( sum(qty) for name in (' + @cols + ') ) p ' execute(@query) 

两个版本都会产生相同的结果。

@bluefeet利用内置的PIVOTfunction提供了非常好的答案。 但是,我经常发现PIVOTUNPIVOT命名混乱,我还没有遇到一个情况,即标准聚合无法达到同样的结果:

 select w.ws_id, w.start_date, w.end_date, [100.00] = isnull(sum(case when c.name='100.00' then cw.qty else null end), 0), [50.00] = isnull(sum(case when c.name='50.00' then cw.qty else null end), 0), [20.00] = isnull(sum(case when c.name='20.00' then cw.qty else null end), 0), [10.00] = isnull(sum(case when c.name='10.00' then cw.qty else null end), 0), [5.00] = isnull(sum(case when c.name='5.00' then cw.qty else null end), 0), [1.00] = isnull(sum(case when c.name='1.00' then cw.qty else null end), 0) from workshift w join currency_by_workshift cw on w.ws_id=cw.ws_id join currency_denom c on cw.cd_id=c.cd_id group by w.ws_id, w.start_date, w.end_date 

如果你想做一个dynamic数据透视表,你只需要build立一个数据透视表的string:

 DECLARE @cols AS NVARCHAR(MAX), @query AS NVARCHAR(MAX) select @cols = stuff(( select replace(',[@name] = isnull(sum(case when c.name=''@name'' then cw.qty else null end), 0)' , '@name', rtrim(name)) from currency_denom order by cd_id for xml path(''), type ).value('.', 'nvarchar(max)') ,1,1,'') select @query = ' select w.ws_id, w.start_date, w.end_date, '+@cols+' from workshift w join currency_by_workshift cw on w.ws_id=cw.ws_id join currency_denom c on cw.cd_id=c.cd_id group by w.ws_id, w.start_date, w.end_date ' execute(@query)