我有一个带有XML列的SQL表。我想将XML元素分离到视图中的各自列中。
我能够使用.value执行此操作,但我并不总是知道元素名称是什么。例如,在下面的选择中缺少c元素。
create table #temp (Id int, Name varchar(32), taskdata xml)
insert into #temp values
(1, 'Fred','<data><a>Red</a><b>Apple</b></data>'),
(2, 'Mary','<data><a>Blue</a><b>Ball</b></data>'),
(3, 'Paul','<data><a>Green</a><b>Tree</b></data>'),
(4, 'Lisa','<data><a>Yellow</a><b>Hat</b><c>House</c></data>')
select Id
,Name
,Taskdata.value('(/data/a)[1]', 'nvarchar(max)') AS a
,Taskdata.value('(/data/b)[1]', 'nvarchar(max)') AS b
from #temp
drop table #temp
我可以使用以下命令获取所有元素名称的列表:
select distinct T.N.value('local-name(.)','nvarchar(64)') ColNames
from #temp
cross apply Taskdata.nodes('//data/*') as T(N)
但是我无法解决如何更换:
Taskdata.value('(/data/a)[1]', 'nvarchar(max)') AS a
更有活力的东西。
答案 0 :(得分:3)
如果您需要完全通用的方法,可以尝试使用动态SQL:
DECLARE @cmd VARCHAR(1000)=
'select Id
,Name' +
(
SELECT DISTINCT',Taskdata.value(''(/data/' + TheNode.value('local-name(.)','nvarchar(64)') + ')[1]'', ''nvarchar(max)'') AS [' + TheNode.value('local-name(.)','nvarchar(64)') + '] '
FROM #temp AS innerT
CROSS APPLY innerT.taskdata.nodes('/data/*') AS ThisIs(TheNode)
FOR XML PATH('')
)
+
'from #temp;'
EXEC (@cmd);
视图无法使用临时表,必须将#temp更改为普通表...
create table temp (Id int, Name varchar(32), taskdata xml)
insert into temp values
(1, 'Fred','<data><a>Red</a><b>Apple</b></data>'),
(2, 'Mary','<data><a>Blue</a><b>Ball</b></data>'),
(3, 'Paul','<data><a>Green</a><b>Tree</b></data>'),
(4, 'Lisa','<data><a>Yellow</a><b>Hat</b><c>House</c></data>')
DECLARE @cmd VARCHAR(1000)=
'CREATE VIEW dbo.SomeName AS select Id
,Name' +
(
SELECT DISTINCT',Taskdata.value(''(/data/' + TheNode.value('local-name(.)','nvarchar(64)') + ')[1]'', ''nvarchar(max)'') AS [' + TheNode.value('local-name(.)','nvarchar(64)') + '] '
FROM temp AS innerT
CROSS APPLY innerT.taskdata.nodes('/data/*') AS ThisIs(TheNode)
FOR XML PATH('')
)
+
'from temp;'
EXEC (@cmd);
GO
SELECT * FROM dbo.SomeName;
GO
drop view dbo.SomeName;
drop table temp;
XML的问题是:你必须知道结构,至少你的数据有一些共同点:是否总有根元素“数据”?是否总有1:n内部元素而没有别的?他们的最大数量是多少?如果你有a和c但没有b,你怎么知道缺少哪个元素?
这是一种方法:
select Id
,Name
,Taskdata.value('/data[1]/*[1]', 'nvarchar(max)') AS a
,Taskdata.value('/data[1]/*[2]', 'nvarchar(max)') AS b
,Taskdata.value('/data[1]/*[3]', 'nvarchar(max)') AS c
from #temp
如果您通过查询
了解内部元素的名称,则会得到相同的结果 ,Taskdata.value('(/data/c)[1]', 'nvarchar(max)') AS c
结果
Id Name a b c
1 Fred Red Apple NULL
2 Mary Blue Ball NULL
3 Paul Green Tree NULL
4 Lisa Yellow Hat House