SQL Server 2008:在多个列上旋转
需要有关SQL Server 2008中pivot子句的帮助。我有一张包含以下信息的表格: 我的表格有9列:SQL Server 2008:在多个列上旋转,sql,sql-server-2008,pivot,Sql,Sql Server 2008,Pivot,需要有关SQL Server 2008中pivot子句的帮助。我有一张包含以下信息的表格: 我的表格有9列:ID,Period\u 1到Period\u 4作为日期(即2013-04、2013-07等)和Amount\u 1到Amount\u 4(即30、40等)。我希望从期间1到期间4的所有不同日期都作为列名,然后透视期间1对应的金额1,期间2对应的金额2,Amount\u 3对应于Period\u 3和Amount\u 4对应于Period\u 4作为行值 这是我目前提出的T-SQL: DE
ID
,Period\u 1
到Period\u 4
作为日期(即2013-04、2013-07等)和Amount\u 1
到Amount\u 4
(即30、40等)。我希望从期间1
到期间4
的所有不同日期都作为列名,然后透视期间1
对应的金额1
,期间2
对应的金额2
,Amount\u 3
对应于Period\u 3
和Amount\u 4
对应于Period\u 4
作为行值
这是我目前提出的T-SQL:
DECLARE @cols AS NVARCHAR(MAX)
,@query AS NVARCHAR(MAX)
SELECT @cols = STUFF((
SELECT DISTINCT ',' + QUOTENAME(ans)
FROM (
SELECT Period_1 AS ans
FROM Booking
UNION
SELECT Period_2 AS ans
FROM Booking
UNION
SELECT Period_3 AS ans
FROM Booking
UNION
SELECT Period_4 AS ans
FROM Booking
) a
FOR XML PATH('')
,TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
SET @query = 'SELECT Id ' + @cols + ' from
(
select Id, Period_1, Amount_1, Period_2, Amount_2
from Booking
) x
pivot
(
max(Amount_1)
for Period_1 in (' + @cols + ')
) p
(
max(Amount_2)
for Period_2 in (' + @cols + ')
) p
'
EXECUTE (@query)
我得到了一个错误:
Msg 265,第16级,状态1,第18行PIVOT运算符中指定的列名“2013-10”与PIVOT参数中的现有列名冲突 有没有办法对包含相同值的多个列执行透视查询?请写一个如何做的例子来回应
我非常感谢你在这方面的帮助。提前谢谢 我认为您必须在字段名中添加一些内容,并为您使用的每个值创建一个
@cols
变量,即:
SELECT @cols1 = STUFF((
SELECT DISTINCT ',' + QUOTENAME(ans)
FROM (SELECT '1_'+CAST(Period_1 AS VARCHAR(12)) AS ans
FROM #test
UNION
SELECT '1_'+CAST(Period_2 AS VARCHAR(12)) AS ans
FROM #test
) a
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,'')
,@cols2 = STUFF((
SELECT DISTINCT ',' + QUOTENAME(ans)
FROM (SELECT '2_'+CAST(Period_1 AS VARCHAR(12)) AS ans
FROM #test
UNION
SELECT '2_'+CAST(Period_2 AS VARCHAR(12)) AS ans
FROM #test
) a
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,'')
然后在数据透视时引用相应的变量,您还必须在字段名称中添加:
(
max(Amount_1)
for '1_'+CAST(Period_1 AS VARCHAR(12)) in (' + @cols1 + ')
) p
可能有一种更聪明的方法可以做到这一点,但我不知道有一种。我认为您必须在字段名中添加一些内容,并为您使用的每个值创建一个
@cols
变量,即:
SELECT @cols1 = STUFF((
SELECT DISTINCT ',' + QUOTENAME(ans)
FROM (SELECT '1_'+CAST(Period_1 AS VARCHAR(12)) AS ans
FROM #test
UNION
SELECT '1_'+CAST(Period_2 AS VARCHAR(12)) AS ans
FROM #test
) a
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,'')
,@cols2 = STUFF((
SELECT DISTINCT ',' + QUOTENAME(ans)
FROM (SELECT '2_'+CAST(Period_1 AS VARCHAR(12)) AS ans
FROM #test
UNION
SELECT '2_'+CAST(Period_2 AS VARCHAR(12)) AS ans
FROM #test
) a
FOR XML PATH(''),TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1,'')
然后在数据透视时引用相应的变量,您还必须在字段名称中添加:
(
max(Amount_1)
for '1_'+CAST(Period_1 AS VARCHAR(12)) in (' + @cols1 + ')
) p
可能有一种更聪明的方法可以做到这一点,但我不知道。你的部分问题是你的数据没有标准化,有
句点1
,金额1
,句点2
,金额2
等列使得查询你的数据非常困难。我的第一个建议是考虑将表结构固定到与以下类似的东西:
create table booking
(
id int,
period datetime,
amount decimal(10, 5)
);
这将允许您为每个id设置多个期间和金额。有其他方法可以设计,但这将让您了解如何修复当前结构
如果您无法修复结构,那么我建议对现有表应用UNPIVOT和PIVOT。UNPIVOT会将多列数据转换为多行,然后您可以将数据透视应用于金额以获得最终结果
UNPIVOT的基本语法如下所示。我使用工会交叉申请,因为我们需要同时取消期限和金额:
select id,
convert(varchar(7), period, 120) period,
amount
from
(
select id,
period_1, period_2, period_3, period_4,
amount_1, amount_2, amount_3, amount_4
from booking
) d
cross apply
(
select period_1, amount_1 union all
select period_2, amount_2 union all
select period_3, amount_3 union all
select period_4, amount_4
) c (period, amount);
看。这将为您提供以下格式的数据:
| ID | PERIOD | AMOUNT |
-------------------------
| 1 | 2013-01 | 30 |
| 1 | 2013-04 | 40 |
| 1 | 2013-07 | 50 |
| 1 | 2013-10 | 60 |
数据采用此格式后,您可以将透视函数应用于Period
列中的值:
select id,
[2013-01], [2013-04], [2013-05],
[2013-07], [2013-08], [2013-10],
[2013-11]
from
(
select id,
convert(varchar(7), period, 120) period,
amount
from
(
select id,
period_1, period_2, period_3, period_4,
amount_1, amount_2, amount_3, amount_4
from booking
) d
cross apply
(
select period_1, amount_1 union all
select period_2, amount_2 union all
select period_3, amount_3 union all
select period_4, amount_4
) c (period, amount)
) src
pivot
(
sum(amount)
for period in ([2013-01], [2013-04], [2013-05],
[2013-07], [2013-08], [2013-10],
[2013-11])
) piv;
看。当然,如果你提前知道这些价值观,上述方法将非常有效。但是如果您不这样做,那么您将希望使用动态SQL来获得结果:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(period)
from
(
select convert(varchar(7), period_1, 120) period, period_1 dt
from booking union all
select convert(varchar(7), period_2, 120) period, period_2 dt
from booking union all
select convert(varchar(7), period_3, 120) period, period_3 dt
from booking union all
select convert(varchar(7), period_4, 120) period, period_4
from booking
) d
group by period, dt
order by dt
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT id, ' + @cols + '
from
(
select id,
convert(varchar(7), period, 120) period,
amount
from
(
select id,
period_1, period_2, period_3, period_4,
amount_1, amount_2, amount_3, amount_4
from booking
) d
cross apply
(
select period_1, amount_1 union all
select period_2, amount_2 union all
select period_3, amount_3 union all
select period_4, amount_4
) c (period, amount)
) src
pivot
(
sum(amount)
for period in (' + @cols + ')
) p '
execute(@query);
看。这些查询将产生类似以下结果:
| ID | 2013-01 | 2013-04 | 2013-05 | 2013-07 | 2013-08 | 2013-10 | 2013-11 |
----------------------------------------------------------------------------
| 1 | 105 | 40 | 86 | 50 | 120 | 60 | 65 |
问题的一部分是您的数据没有标准化,具有
Period\u 1
、Amount\u 1
、Period\u 2
、Amount\u 2
等列的数据查询非常困难。我的第一个建议是考虑将表结构固定到与以下类似的东西:
create table booking
(
id int,
period datetime,
amount decimal(10, 5)
);
这将允许您为每个id设置多个期间和金额。有其他方法可以设计,但这将让您了解如何修复当前结构
如果您无法修复结构,那么我建议对现有表应用UNPIVOT和PIVOT。UNPIVOT会将多列数据转换为多行,然后您可以将数据透视应用于金额以获得最终结果
UNPIVOT的基本语法如下所示。我使用工会交叉申请,因为我们需要同时取消期限和金额:
select id,
convert(varchar(7), period, 120) period,
amount
from
(
select id,
period_1, period_2, period_3, period_4,
amount_1, amount_2, amount_3, amount_4
from booking
) d
cross apply
(
select period_1, amount_1 union all
select period_2, amount_2 union all
select period_3, amount_3 union all
select period_4, amount_4
) c (period, amount);
看。这将为您提供以下格式的数据:
| ID | PERIOD | AMOUNT |
-------------------------
| 1 | 2013-01 | 30 |
| 1 | 2013-04 | 40 |
| 1 | 2013-07 | 50 |
| 1 | 2013-10 | 60 |
数据采用此格式后,您可以将透视函数应用于Period
列中的值:
select id,
[2013-01], [2013-04], [2013-05],
[2013-07], [2013-08], [2013-10],
[2013-11]
from
(
select id,
convert(varchar(7), period, 120) period,
amount
from
(
select id,
period_1, period_2, period_3, period_4,
amount_1, amount_2, amount_3, amount_4
from booking
) d
cross apply
(
select period_1, amount_1 union all
select period_2, amount_2 union all
select period_3, amount_3 union all
select period_4, amount_4
) c (period, amount)
) src
pivot
(
sum(amount)
for period in ([2013-01], [2013-04], [2013-05],
[2013-07], [2013-08], [2013-10],
[2013-11])
) piv;
看。当然,如果你提前知道这些价值观,上述方法将非常有效。但是如果您不这样做,那么您将希望使用动态SQL来获得结果:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(period)
from
(
select convert(varchar(7), period_1, 120) period, period_1 dt
from booking union all
select convert(varchar(7), period_2, 120) period, period_2 dt
from booking union all
select convert(varchar(7), period_3, 120) period, period_3 dt
from booking union all
select convert(varchar(7), period_4, 120) period, period_4
from booking
) d
group by period, dt
order by dt
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT id, ' + @cols + '
from
(
select id,
convert(varchar(7), period, 120) period,
amount
from
(
select id,
period_1, period_2, period_3, period_4,
amount_1, amount_2, amount_3, amount_4
from booking
) d
cross apply
(
select period_1, amount_1 union all
select period_2, amount_2 union all
select period_3, amount_3 union all
select period_4, amount_4
) c (period, amount)
) src
pivot
(
sum(amount)
for period in (' + @cols + ')
) p '
execute(@query);
看。这些查询将产生类似以下结果:
| ID | 2013-01 | 2013-04 | 2013-05 | 2013-07 | 2013-08 | 2013-10 | 2013-11 |
----------------------------------------------------------------------------
| 1 | 105 | 40 | 86 | 50 | 120 | 60 | 65 |
谢谢你的回答!谢谢你的回答!