Ser*_*gei 117 sql t-sql sql-server unpivot
寻找优雅(或任何)解决方案将列转换为行.
这是一个例子:我有一个包含以下模式的表:
[ID] [EntityID] [Indicator1] [Indicator2] [Indicator3] ... [Indicator150]
Run Code Online (Sandbox Code Playgroud)
以下是我想得到的结果:
[ID] [EntityId] [IndicatorName] [IndicatorValue]
Run Code Online (Sandbox Code Playgroud)
结果值将是:
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'
Run Code Online (Sandbox Code Playgroud)
等等..
这有意义吗?您对在T-SQL中查看的位置以及如何完成它有什么建议吗?
Tar*_*ryn 222
您可以使用UNPIVOT函数将列转换为行:
select id, entityId,
indicatorname,
indicatorvalue
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
Run Code Online (Sandbox Code Playgroud)
请注意,要取消激活的列的数据类型必须相同,因此在应用unpivot之前可能必须转换数据类型.
您还可以使用CROSS APPLY
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);
Run Code Online (Sandbox Code Playgroud)
根据您的SQL Server版本,您甚至可以将CROSS APPLY与VALUES子句一起使用:
select id, entityid,
indicatorname,
indicatorvalue
from yourtable
cross apply
(
values
('Indicator1', Indicator1),
('Indicator2', Indicator2),
('Indicator3', Indicator3),
('Indicator4', Indicator4)
) c (indicatorname, indicatorvalue);
Run Code Online (Sandbox Code Playgroud)
最后,如果您有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;
Run Code Online (Sandbox Code Playgroud)
Rom*_*kar 20
如果您有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%'
Run Code Online (Sandbox Code Playgroud)
您也可以编写动态SQL,但我更喜欢xml - 对于动态SQL,您必须具有直接从表中选择数据的权限,这并不总是一个选项.
更新
由于评论中有一个很大的火焰,我想我会添加一些xml /动态SQL的优点和缺点.我会尽量做到尽可能客观,不要提到优雅和丑陋.如果您有任何其他优点和缺点,请编辑答案或写下评论
缺点
利弊
inserted
和deleted
表(根本不可能动态);Joh*_*tti 12
只是因为我没有看到它被提及。
如果是 2016 年以上,这里还有另一个选项可以动态反转数据,而无需实际使用动态 SQL。
例子
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
Run Code Online (Sandbox Code Playgroud)
退货
ID Item Value
1 Col1 A
1 Col2 B
2 Col1 R
2 Col2 C
3 Col1 X
3 Col2 D
Run Code Online (Sandbox Code Playgroud)
为了帮助新读者,我创建了一个例子来更好地理解@ bluefeet关于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;
Run Code Online (Sandbox Code Playgroud)
小智 5
我需要一个解决方案来将 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)
Run Code Online (Sandbox Code Playgroud)
如您所见,我将行转换为 XML(子查询选择 i,* for xml raw,这会将所有列转换为一个 xml 列)
然后我将一个函数交叉应用到该列的每个 XML 属性,以便每个属性获取一行。
总的来说,这会将列转换为行,而无需知道列名,也无需使用动态 SQL。对于我的目的来说它已经足够快了。
(编辑:我刚刚看到Roman Pekar在上面回答,他也在做同样的事情。我首先使用带有游标的动态sql触发器,这比这个解决方案慢10到100倍,但也许这是由游标引起的,而不是由游标引起的动态sql。无论如何,这个解决方案非常简单且通用,因此它绝对是一个选项)。
我在此位置留下此评论,因为我想在有关完整审核触发器的帖子中引用此解释,您可以在这里找到:/sf/answers/3066020051/