我确定我在这里遗漏了一些东西。
我有这样的数据集:
FK RowNumber Value Type Status 1 1 aaaaa A New 1 2 bbbbb B Good 1 3 ccccc A Bad 1 4 ddddd C Good 1 5 eeeee B Good 2 1 fffff C Bad 2 2 ggggg A New 2 3 hhhhh C Bad 3 1 iiiii A Good 3 2 jjjjj A Good
我想查询前3个结果并将它们转换为列,因此最终结果集如下所示:
FK Value1 Type1 Status1 Value2 Type2 Status2 Value3 Type3 Status3 1 aaaaa A New bbbbb B Good ccccc A Bad 2 fffff C Bad ggggg A New hhhhh C Bad 3 iiiii A Good jjjjj A Good
如何在SQL Server 2005中完成此操作?
我一直在尝试使用PIVOT,但我仍然不熟悉该关键字,无法按照我想要的方式运行。
SELECT * --Id, [1], [2], [3]
FROM
(
SELECT Id, Value, Type, Status
, ROW_NUMBER() OVER (PARTITION BY Id ORDER Status, Type) as [RowNumber]
FROM MyTable
) as T
PIVOT
(
-- I know this section doesn't work. I'm still trying to figure out PIVOT
MAX(T.Value) FOR RowNumber IN ([1], [2], [3]),
MAX(T.Type) FOR RowNumber IN ([1], [2], [3]),
MAX(T.Status) FOR RowNumber IN ([1], [2], [3])
) AS PivotTable;
我的实际数据集比这更复杂,我需要前10条记录,而不是前3条,所以我不想只为每条记录做CASE WHEN RowNumber = X THEN...
。
更新
我测试了下面的所有答案,发现大多数答案看起来大致相同,在较小的数据集(大约3k记录)中没有明显的性能差异,但是在针对较大的数据集运行查询时存在细微差别。
以下是我使用80,000条记录并查询前10行中5列的测试结果,因此我的最终结果集为50列+ Id
列。我建议你自己测试一下,以决定哪一个最适合你和你的环境。
bluefoot's answer对数据进行取消和重新转动,平均速度最快,大约为12秒。我也很喜欢这个答案,因为我觉得最容易阅读和维护。
Aaron's answer和koderoid's answer都建议使用MAX(CASE WHEN RowNumber = X THEN ...)
,并且在13秒左右平均落后于平均值。
Rodney's answer使用多个PIVOT
语句平均大约16秒,尽管使用较少的PIVOT语句可能会更快(我的测试有5个)。
建议使用CTE和OUTER APPLY
的{{3}}的前半部分是最慢的。我不知道运行需要多长时间,因为我在2分钟后取消了它,那是大约3k记录,3行和3列,而不是80k记录,10行和5列。
答案 0 :(得分:7)
您可以尝试在三个单独的pivot语句中执行pivot。请试一试:
SELECT Id
,MAX(S1) [Status 1]
,MAX(T1) [Type1]
,MAX(V1) [Value1]
--, Add other columns
FROM
(
SELECT Id, Value , Type, Status
, 'S' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Status_RowNumber]
, 'T' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Type_RowNumber]
, 'V' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Value_RowNumber]
FROM MyTable
) as T
PIVOT
(
MAX(Status) FOR Status_RowNumber IN ([S1], [S2], [S3],[S4],[S5],[S6],[S7],[S8],[S9],[S10])
)AS StatusPivot
PIVOT(
MAX(Type) FOR Type_RowNumber IN ([T1], [T2], [T3],[T4],[T5],[T6],[T7],[T8],[T9],[T10])
)AS Type_Pivot
PIVOT(
MAX(Value) FOR Value_RowNumber IN ([V1], [V2], [V3],[V4],[V5],[V6],[V7],[V8],[V9],[V10])
)AS Value_Pivot
GROUP BY Id
我不知道选择前十条记录的标准的全部范围,但这会产生和输出,使您更接近您的答案。
答案 1 :(得分:7)
您可以执行UNPIVOT
,然后执行PIVOT
数据。这可以静态或动态地完成:
静态版本:
select *
from
(
select fk, col + cast(rownumber as varchar(1)) new_col,
val
from
(
select fk, rownumber, value, cast(type as varchar(10)) type,
status
from yourtable
) x
unpivot
(
val
for col in (value, type, status)
) u
) x1
pivot
(
max(val)
for new_col in
([value1], [type1], [status1],
[value2], [type2], [status2],
[value3], [type3])
) p
动态版本,这会将列表列为unpivot
,然后在运行时获取pivot
:
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX),
@colsPivot as NVARCHAR(MAX)
select @colsUnpivot = stuff((select ','+quotename(C.name)
from sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('fk', 'rownumber')
for xml path('')), 1, 1, '')
select @colsPivot = STUFF((SELECT ','
+ quotename(c.name
+ cast(t.rownumber as varchar(10)))
from yourtable t
cross apply
sys.columns as C
where C.object_id = object_id('yourtable') and
C.name not in ('fk', 'rownumber')
group by c.name, t.rownumber
order by t.rownumber
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query
= 'select *
from
(
select fk, col + cast(rownumber as varchar(10)) new_col,
val
from
(
select fk, rownumber, value, cast(type as varchar(10)) type,
status
from yourtable
) x
unpivot
(
val
for col in ('+ @colsunpivot +')
) u
) x1
pivot
(
max(val)
for new_col in
('+ @colspivot +')
) p'
exec(@query)
两者都会生成相同的结果,但如果您不提前知道列数,则动态效果会很好。
动态版本在假设rownumber已经是数据集的一部分的情况下工作。
答案 2 :(得分:2)
;WITH a AS
(
SELECT Id, Value, Type, Status,
n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
FROM dbo.MyTable
)
SELECT a.Id,
Value1 = a.Value, Type1 = a.[Type], Status1 = a.[Status],
Value2 = b.Value, Type2 = b.[Type], Status2 = b.[Status],
Value3 = c.Value, Type3 = c.[Type], Status3 = c.[Status]
FROM a
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = a.n + 1 AND id = a.id) AS b
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = b.n + 1 AND id = b.id) AS c
WHERE a.n = 1
ORDER BY a.Id;
- 或 -
;WITH a AS
(
SELECT Id, Value, [Type], [Status],
n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
FROM dbo.MyTable
)
SELECT Id,
Value1 = MAX(CASE WHEN n = 1 THEN Value END),
Type1 = MAX(CASE WHEN n = 1 THEN [Type] END),
Status1 = MAX(CASE WHEN n = 1 THEN [Status] END),
Value2 = MAX(CASE WHEN n = 2 THEN Value END),
Type2 = MAX(CASE WHEN n = 2 THEN [Type] END),
Status2 = MAX(CASE WHEN n = 2 THEN [Status] END),
Value3 = MAX(CASE WHEN n = 3 THEN Value END),
Type3 = MAX(CASE WHEN n = 3 THEN [Type] END),
Status3 = MAX(CASE WHEN n = 3 THEN [Status] END)
FROM a
GROUP BY Id
ORDER BY a.Id;
答案 3 :(得分:1)
这可能适合你,虽然它不优雅。
select aa.FK_Id
, isnull(max(aa.Value1), '') as Value1
, isnull(max(aa.Type1), '') as Type1
, isnull(max(aa.Status1), '') as Status1
, isnull(max(aa.Value2), '') as Value2
, isnull(max(aa.Type2), '') as Type2
, isnull(max(aa.Status2), '') as Status2
, isnull(max(aa.Value3), '') as Value3
, isnull(max(aa.Type3), '') as Type3
, isnull(max(aa.Status3), '') as Status3
from
(
select FK_Id
, case when RowNumber = 1 then Value else null end as Value1
, case when RowNumber = 1 then [Type] else null end as Type1
, case when RowNumber = 1 then [Status] else null end as Status1
, case when RowNumber = 2 then Value else null end as Value2
, case when RowNumber = 2 then [Type] else null end as Type2
, case when RowNumber = 2 then [Status] else null end as Status2
, case when RowNumber = 3 then Value else null end as Value3
, case when RowNumber = 3 then [Type] else null end as Type3
, case when RowNumber = 3 then [Status] else null end as Status3
from Table1
) aa
group by aa.FK_Id
答案 4 :(得分:1)
尝试这样的事情:
declare @rowCount int
set @rowCount = 10
declare @isNullClause varchar(4024)
set @isnullClause = ''
declare @caseClause varchar(4024)
set @caseClause = ''
declare @i int
set @i = 1
while(@i <= @rowCount) begin
set @isnullClause = @isNullClause +
' , max(aa.Value' + CAST(@i as varchar(3)) + ') as Value' + CAST(@i as varchar(3)) +
' , max(aa.Type' + CAST(@i as varchar(3)) + ') as Type' + CAST(@i as varchar(3)) +
' , max(aa.Status' + CAST(@i as varchar(3)) + ') as Status' + CAST(@i as varchar(3)) + ' ';
set @caseClause = @caseClause +
' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Value else null end as Value' + CAST(@i as varchar(3)) +
' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Type else null end as Type' + CAST(@i as varchar(3)) +
' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Status else null end as Status' + CAST(@i as varchar(3)) + ' '
set @i = @i + 1;
end
declare @sql nvarchar(4000)
set @sql = 'select aa.FK_Id ' + @isnullClause + ' from ( select FK_Id '
+ @caseClause + ' from Table1) aa group by aa.FK_Id '
exec SP_EXECUTESQL @sql