基于 SQL Server 中的一列透视多列
Posted
技术标签:
【中文标题】基于 SQL Server 中的一列透视多列【英文标题】:Pivot multiple columns based on one column in SQL Server 【发布时间】:2013-08-04 02:42:31 【问题描述】:我在 SQL Server 2008R2 中有以下源表和目标表。如何在 TSQL 中进行数据透视以将 SourceTbl
转换为 DestTbl
?希望 empIndex 能以某种方式在支点上有所帮助。
SourceTbl
empId empIndex empState empStDate empEndDate
========================================================
10 1 AL 1/1/2012 12/1/2012
10 2 FL 2/1/2012 2/1/2013
15 1 FL 3/20/2012 1/1/2099
DestTbl
empId empState1 empState1StDate empState1EndDt empState2 empState2StDate empState2EndDt
=========================================================================================================
10 AL 1/1/2012 12/1/2012 FL 2/1/2012 2/1/2013
15 FL 3/20/2012 1/1/2099 NULL NULL NULL
【问题讨论】:
pivot 在 msaccess 中也称为变换。您的问题是独一无二的,因为它在结果单元格中也有文本(不是整数)。聚合函数仍然必须应用,在这种情况下 MIN() 应该在 Text 值上运行良好,即使只有 1 个文本值。 【参考方案1】:由于您使用的是 SQL Server,因此您可以通过多种不同的方式将行转换为列。您可以将聚合函数与 CASE 表达式一起使用:
select empid,
max(case when empindex = 1 then empstate end) empState1,
max(case when empindex = 1 then empStDate end) empStDate1,
max(case when empindex = 1 then empEndDate end) empEndDate1,
max(case when empindex = 2 then empstate end) empState2,
max(case when empindex = 2 then empStDate end) empStDate2,
max(case when empindex = 2 then empEndDate end) empEndDate2
from sourcetbl
group by empid;
见SQL Fiddle with Demo。
如果您想使用 PIVOT 函数来获取结果,那么我建议您首先取消旋转列 empState
、empStDate
和 empEndDate
,以便您首先拥有多行。您可以使用 UNPIVOT 函数或 CROSS APPLY 来转换代码将是的数据:
select empid, col+cast(empindex as varchar(10)) col, value
from sourcetbl
cross apply
(
select 'empstate', empstate union all
select 'empstdate', convert(varchar(10), empstdate, 120) union all
select 'empenddate', convert(varchar(10), empenddate, 120)
) c (col, value);
见Demo。取消数据透视后,您可以应用 PIVOT 函数,这样最终代码将是:
select empid,
empState1, empStDate1, empEndDate1,
empState2, empStDate2, empEndDate2
from
(
select empid, col+cast(empindex as varchar(10)) col, value
from sourcetbl
cross apply
(
select 'empstate', empstate union all
select 'empstdate', convert(varchar(10), empstdate, 120) union all
select 'empenddate', convert(varchar(10), empenddate, 120)
) c (col, value)
) d
pivot
(
max(value)
for col in (empState1, empStDate1, empEndDate1,
empState2, empStDate2, empEndDate2)
) piv;
见SQL Fiddle with Demo。
如果empindex
数量有限,上述版本会很好用,但如果没有,则可以使用动态 SQL:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(col+cast(empindex as varchar(10)))
from SourceTbl
cross apply
(
select 'empstate', 1 union all
select 'empstdate', 2 union all
select 'empenddate', 3
) c (col, so)
group by col, so, empindex
order by empindex, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT empid,' + @cols + '
from
(
select empid, col+cast(empindex as varchar(10)) col, value
from sourcetbl
cross apply
(
select ''empstate'', empstate union all
select ''empstdate'', convert(varchar(10), empstdate, 120) union all
select ''empenddate'', convert(varchar(10), empenddate, 120)
) c (col, value)
) x
pivot
(
max(value)
for col in (' + @cols + ')
) p '
execute sp_executesql @query;
见 SQL Fiddle with Demo
您可以使用这些查询 INSERT INTO 您的DestTbl
,或者您现在可以通过查询来获得所需的结果,而不是以这种格式存储数据。
这些查询以以下格式放置数据:
| EMPID | EMPSTATE1 | EMPSTDATE1 | EMPENDDATE1 | EMPSTATE2 | EMPSTDATE2 | EMPENDDATE2 |
---------------------------------------------------------------------------------------
| 10 | AL | 2012-01-01 | 2012-12-01 | FL | 2012-02-01 | 2013-02-01 |
| 15 | FL | 2012-03-20 | 2099-01-01 | (null) | (null) | (null) |
【讨论】:
我之前已经看到 FOR XML 使用了很多......但是为了最好地使用:STUFF QUOTENAME 和 NVARCHAR(MAX) 在 PIVOT 中的最佳用途:) MSACCESS 称之为TRANSFORM(不知道为什么该命令从未进入 SQL 服务器)【参考方案2】:哇,这比我想象的要复杂,但我确实让它工作得很好!谢谢。这是我的最终版本。 TextKey 包含要转换为列的数据,TextValue 是每个单元格内的值。
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT distinct ', ' + QUOTENAME(TextKey)
from #SourceTbl
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT FromEntityID, DisplayName, ' + @cols + '
FROM
(
select FromEntityID, DisplayName, TextKey, TextValue
from #SourceTbl
) x
pivot
(
min(TextValue)
for TextKey in (' + @cols + ')
) p
ORDER BY FromEntityID
'
execute(@query)
【讨论】:
以上是关于基于 SQL Server 中的一列透视多列的主要内容,如果未能解决你的问题,请参考以下文章