SQL Server:从列到行
正在寻找将列转换为行的优雅(或任何)解决方案 下面是一个示例:我有一个具有以下模式的表:SQL Server:从列到行,sql,sql-server,tsql,unpivot,Sql,Sql Server,Tsql,Unpivot,正在寻找将列转换为行的优雅(或任何)解决方案 下面是一个示例:我有一个具有以下模式的表: [ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150] 以下是我希望得到的结果: [ID] [EntityId] [IndicatorName] [IndicatorValue] 结果值为: 1 1 'Indicator1' 'Value of Indicator 1 for entity 1' 2 1 'Ind
[ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150]
以下是我希望得到的结果:
[ID] [EntityId] [IndicatorName] [IndicatorValue]
结果值为:
1 1 'Indicator1' 'Value of Indicator 1 for entity 1'
2 1 'Indicator2' 'Value of Indicator 2 for entity 1'
3 1 'Indicator3' 'Value of Indicator 3 for entity 1'
4 2 'Indicator1' 'Value of Indicator 1 for entity 2'
等等
这有意义吗?对于在T-SQL中查找和如何完成,您有什么建议吗 您可以使用该函数将列转换为行:
select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
注意,要取消PIVOT的列的数据类型必须相同,因此您可能必须在应用取消PIVOT之前转换数据类型
您还可以将交叉应用
与UNION ALL一起使用来转换列:
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
select 'Indicator1', Indicator1 union all
select 'Indicator2', Indicator2 union all
select 'Indicator3', Indicator3 union all
select 'Indicator4', Indicator4
) c (indicatorname, indicatorvalue);
根据您的SQL Server版本,您甚至可以使用带VALUES子句的交叉应用:
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
values
('Indicator1', Indicator1),
('Indicator2', Indicator2),
('Indicator3', Indicator3),
('Indicator4', Indicator4)
) c (indicatorname, indicatorvalue);
最后,如果要取消PIVOT的列有150个,并且不想硬编码整个查询,那么可以使用动态sql生成sql语句:
DECLARE @colsUnpivot AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @colsUnpivot
= stuff((select ','+quotename(C.column_name)
from information_schema.columns as C
where C.table_name = 'yourtable' and
C.column_name like 'Indicator%'
for xml path('')), 1, 1, '')
set @query
= 'select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in ('+ @colsunpivot +')
) u'
exec sp_executesql @query;
如果你有150列,那么我认为UNPIVOT不是一个选项。所以您可以使用xml技巧
;with CTE1 as (
select ID, EntityID, (select t.* for xml raw('row'), type) as Data
from temp1 as t
), CTE2 as (
select
C.id, C.EntityID,
F.C.value('local-name(.)', 'nvarchar(128)') as IndicatorName,
F.C.value('.', 'nvarchar(max)') as IndicatorValue
from CTE1 as c
outer apply c.Data.nodes('row/@*') as F(C)
)
select * from CTE2 where IndicatorName like 'Indicator%'
您也可以编写动态SQL,但我更喜欢xml—对于动态SQL,您必须具有直接从表中选择数据的权限,而这并不总是一个选项
更新由于评论中出现了激烈的争论,我想我将添加一些xml/动态SQL的优点和缺点。我会尽量客观,不要提优雅和丑陋。如果你有任何其他的优点和缺点,编辑答案或写评论 缺点
ID Item Value
1 Col1 A
1 Col2 B
2 Col1 R
2 Col2 C
3 Col1 X
3 Col2 D
- 它不如动态SQL快,粗略的测试告诉我xml比动态SQL慢大约2.5倍(这是对250000行表的一次查询,所以这个估计是不精确的)。如果您愿意,您可以自己进行比较,这里的示例是,在100000行上,它是29s(xml)和14s(动态)李>
- 可能对于不熟悉xpath的人来说,更难理解李>
ID Item Value
1 Col1 A
1 Col2 B
2 Col1 R
2 Col2 C
3 Col1 X
3 Col2 D
- 它的作用域与其他查询的作用域相同,这可能非常方便。我想到了几个例子
- 您可以在触发器中查询
和插入的
表(使用dynamic根本不可能)李>删除的
- 用户不必具有直接从表中选择的权限。我的意思是,如果您有存储过程层,并且用户有运行sp的权限,但没有直接查询表的权限,那么您仍然可以在存储过程中使用此查询李>
- 您可以查询在您的作用域中填充的表变量(要将其传递到动态SQL中,您必须将其改为临时表,或者创建类型并将其作为参数传递到动态SQL中
- 您可以在触发器中查询
- 您可以在函数(标量或表值)内部执行此查询
声明@TableName varchar(max)=NULL
选择@TableName=COALESCE(@TableName+,,'')+t.TABLE_CATALOG+'.+t.TABLE_SCHEMA+'.+o.Name
从sysindex开始
将系统对象作为o在i.id=o.id上进行内部连接
内部联接信息\u SCHEMA.TABLES T ON T.TABLE\u NAME=o.NAME
其中i.indid<2
和OBJECTPROPERTY(o.id,'IsMSShipped')=0
和i.rowcnt>350
和o.xtype!='TF'
按o.name ASC订购
打印@tablename
您可以获得行数>350的表列表。您可以在表的解决方案列表中看到行。我需要一个解决方案在Microsoft SQL Server中将列转换为行,而不知道列名称(在触发器中使用)和动态SQL(动态SQL在触发器中使用速度太慢) 我终于找到了这个解决方案,效果很好:
SELECT
insRowTbl.PK,
insRowTbl.Username,
attr.insRow.value('local-name(.)', 'nvarchar(128)') as FieldName,
attr.insRow.value('.', 'nvarchar(max)') as FieldValue
FROM ( Select
i.ID as PK,
i.LastModifiedBy as Username,
convert(xml, (select i.* for xml raw)) as insRowCol
FROM inserted as i
) as insRowTbl
CROSS APPLY insRowTbl.insRowCol.nodes('/row/@*') as attr(insRow)
如您所见,我将行转换为XML(子查询选择I,*对于XMLRAW,这将所有列转换为一个XML列)
然后,我对该列的每个XML属性交叉应用一个函数,以便每个属性得到一行
总的来说,这可以将列转换为行,而不需要知道列名,也不需要使用动态sql
(编辑:我刚刚看到上面罗曼·佩卡尔的回答,他也在做同样的事情。
我首先使用了带有游标的动态sql触发器,这比这个解决方案慢10到100倍,但可能是游标导致的,而不是动态sql。无论如何,这个解决方案非常简单(通用,所以它肯定是一个选项)
我把这篇评论留在这里,因为我想在我的帖子中引用关于完整审计触发的解释,你可以在这里找到:为了帮助新读者,我创建了一个例子来更好地理解@BlueFoots关于UNPIVOT的回答
SELECT id
,entityId
,indicatorname
,indicatorvalue
FROM (VALUES
(1, 1, 'Value of Indicator 1 for entity 1', 'Value of Indicator 2 for entity 1', 'Value of Indicator 3 for entity 1'),
(2, 1, 'Value of Indicator 1 for entity 2', 'Value of Indicator 2 for entity 2', 'Value of Indicator 3 for entity 2'),
(3, 1, 'Value of Indicator 1 for entity 3', 'Value of Indicator 2 for entity 3', 'Value of Indicator 3 for entity 3'),
(4, 2, 'Value of Indicator 1 for entity 4', 'Value of Indicator 2 for entity 4', 'Value of Indicator 3 for entity 4')
) AS Category(ID, EntityId, Indicator1, Indicator2, Indicator3)
UNPIVOT
(
indicatorvalue
FOR indicatorname IN (Indicator1, Indicator2, Indicator3)
) UNPIV;
只是因为我没有看到它被提及 如果是2016+,这里还有另一个选项,可以在不实际使用动态SQL的情况下动态取消PIVOT数据 示例
Declare @YourTable Table ([ID] varchar(50),[Col1] varchar(50),[Col2] varchar(50))
Insert Into @YourTable Values
(1,'A','B')
,(2,'R','C')
,(3,'X','D')
Select A.[ID]
,Item = B.[Key]
,Value = B.[Value]
From @YourTable A
Cross Apply ( Select *
From OpenJson((Select A.* For JSON Path,Without_Array_Wrapper ))
Where [Key] not in ('ID','Other','Columns','ToExclude')
) B
返回
ID Item Value
1 Col1 A
1 Col2 B
2 Col1 R
2 Col2 C
3 Col1 X
3 Col2 D
与此相反的是将列展平为csv eg 从字符串_SPLIT('Akio,Hiraku,Kazuo',',')中选择字符串_AGG([value],','))
您研究过了吗?最后是BlueFoot的解决方案。优雅而实用。非常感谢大家。您使用XML选择哪些不需要从表中选择数据的数据?例如,您可以决定不授予用户从表中选择数据的权限,而只授予使用表的存储过程的权限,所以我可以在过程中选择xml,但是如果我想使用动态SQL,我必须使用一些变通方法如果你想让你的用户能够执行代码,你必须给他们执行代码所需的任何访问权限。不要为了让你的答案听起来更好而编造不存在的需求(你也不必对相互竞争的答案发表评论就能看到你的答案——如果他们找到了答案,他们也可以找到你的答案)