sql 中来自 openxml 的动态列和记录
Dynamic columns and records from openxml in sql
我有一个 xml 文档,非常简单明了。唯一的区别是元素可以改变。有一次我可能会:
<data><PersonalInfo>
<Person><FirstName>Bob</FirstName><LastName>Smith</LastName></Person>
<Person><FirstName>John</FirstName><LastName>Doe</LastName></Person>
</PersonalInfo></data>
下次我可能有:
<data><AddressInfo>
<Address><City>Cleveland</City><State>OH</State></Address>
<Address><City>Chicago</City><State>IL</State></Address>
</AddressInfo></data>
我想写一个 select 语句来生成一个动态的 table,具体取决于我目前获得的 xml 文档。
例如:对于第一个:
First Name Last Name
------------------------
Bob Smith
John Doe
Etc...
第二个
City State
-----------------------
Cleveland OH
Chicago IL
Etc...
这 2 个示例没有任何关联(Bob 不是来自克利夫兰等...)
我只想使用相同的代码生成两个 tables...取决于 xml 文档。唯一的区别是节点引用,当然:
Example 1: data/PersonalInfo/Person*
Example 2: data/AddressInfo/Address*
我不想合并或更改 xml 文档结构中的任何内容。
它们就是它们要进来的东西。我如何引用每一个来创建上面两个不同的 tables - 每个进来的 xml 文档都将在一个单独的 运行 存储过程中。但它将是相同的存储过程。非常感谢任何帮助,在此先感谢!
您可以创建一个列映射 table,存储任意数量的 'mapping templates'...(缺少更好的描述)
因此您的新 table 具有以下值:
TYPE ORDER COLUMNNAME
Person 1 FirstName
Person 2 LastName
Address 1 City
Address 2 State
并且您使用此 table 加载实际的 xml 节点名称并使用循环从 XML 收集值。
这可行吗?您可以在 SQL 中制作和阅读此 table 吗?
这样试试:
CREATE PROCEDURE dbo.TransformPlainXML(@InputXml XML)
AS
BEGIN
DECLARE @PivotColumns NVARCHAR(MAX);
WITH DistinctElementNames AS
(
SELECT DISTINCT '[' + Element.value('fn:local-name(.)','varchar(max)') + ']' AS ElementName
FROM @InputXml.nodes('/data/*/*/*') As One(Element)
)
SELECT @PivotColumns = STUFF(
(
SELECT ',' + ElementName
FROM DistinctElementNames
FOR XML PATH('')
),1,1,'');
DECLARE @cmd NVARCHAR(MAX)=
'WITH Lines AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowIndex
,Line.query(''.'') AS OneLine
FROM @xml.nodes(''/data/*/*'') As One(Line)
)
SELECT p.*
FROM
(
SELECT RowIndex
,Element.value(''.[1]'',''varchar(max)'') AS ElementValue
,Element.value(''fn:local-name(.)'',''varchar(max)'') AS ElementName
FROM Lines
CROSS APPLY OneLine.nodes(''./*/*'') AS The(Element)
) AS tbl
PIVOT
(
MIN(ElementValue) FOR ElementName IN(' + @PivotColumns + ')
) AS p
';
EXECUTE sp_executesql @cmd,N'@xml XML',@xml=@InputXml;
END
GO
然后像这样测试
declare @xml1 XML=
'<data>
<PersonalInfo>
<Person>
<FirstName>Bob</FirstName>
<LastName>Smith</LastName>
</Person>
<Person>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
</Person>
</PersonalInfo>
</data>';
EXEC TransformPlainXML @xml1;
declare @xml2 XML=
'<data>
<AddressInfo>
<Address>
<City>Cleveland</City>
<State>OH</State>
</Address>
<Address>
<City>Chicago</City>
<State>IL</State>
</Address>
</AddressInfo>
</data>';
EXEC TransformPlainXML @xml2;
两个结果:
RowIndex FirstName LastName
1 Bob Smith
2 John Doe
和
RowIndex City State
1 Cleveland OH
2 Chicago IL
我有一个 xml 文档,非常简单明了。唯一的区别是元素可以改变。有一次我可能会:
<data><PersonalInfo>
<Person><FirstName>Bob</FirstName><LastName>Smith</LastName></Person>
<Person><FirstName>John</FirstName><LastName>Doe</LastName></Person>
</PersonalInfo></data>
下次我可能有:
<data><AddressInfo>
<Address><City>Cleveland</City><State>OH</State></Address>
<Address><City>Chicago</City><State>IL</State></Address>
</AddressInfo></data>
我想写一个 select 语句来生成一个动态的 table,具体取决于我目前获得的 xml 文档。
例如:对于第一个:
First Name Last Name
------------------------
Bob Smith
John Doe
Etc...
第二个
City State
-----------------------
Cleveland OH
Chicago IL
Etc...
这 2 个示例没有任何关联(Bob 不是来自克利夫兰等...)
我只想使用相同的代码生成两个 tables...取决于 xml 文档。唯一的区别是节点引用,当然:
Example 1: data/PersonalInfo/Person*
Example 2: data/AddressInfo/Address*
我不想合并或更改 xml 文档结构中的任何内容。 它们就是它们要进来的东西。我如何引用每一个来创建上面两个不同的 tables - 每个进来的 xml 文档都将在一个单独的 运行 存储过程中。但它将是相同的存储过程。非常感谢任何帮助,在此先感谢!
您可以创建一个列映射 table,存储任意数量的 'mapping templates'...(缺少更好的描述)
因此您的新 table 具有以下值:
TYPE ORDER COLUMNNAME
Person 1 FirstName
Person 2 LastName
Address 1 City
Address 2 State
并且您使用此 table 加载实际的 xml 节点名称并使用循环从 XML 收集值。
这可行吗?您可以在 SQL 中制作和阅读此 table 吗?
这样试试:
CREATE PROCEDURE dbo.TransformPlainXML(@InputXml XML)
AS
BEGIN
DECLARE @PivotColumns NVARCHAR(MAX);
WITH DistinctElementNames AS
(
SELECT DISTINCT '[' + Element.value('fn:local-name(.)','varchar(max)') + ']' AS ElementName
FROM @InputXml.nodes('/data/*/*/*') As One(Element)
)
SELECT @PivotColumns = STUFF(
(
SELECT ',' + ElementName
FROM DistinctElementNames
FOR XML PATH('')
),1,1,'');
DECLARE @cmd NVARCHAR(MAX)=
'WITH Lines AS
(
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS RowIndex
,Line.query(''.'') AS OneLine
FROM @xml.nodes(''/data/*/*'') As One(Line)
)
SELECT p.*
FROM
(
SELECT RowIndex
,Element.value(''.[1]'',''varchar(max)'') AS ElementValue
,Element.value(''fn:local-name(.)'',''varchar(max)'') AS ElementName
FROM Lines
CROSS APPLY OneLine.nodes(''./*/*'') AS The(Element)
) AS tbl
PIVOT
(
MIN(ElementValue) FOR ElementName IN(' + @PivotColumns + ')
) AS p
';
EXECUTE sp_executesql @cmd,N'@xml XML',@xml=@InputXml;
END
GO
然后像这样测试
declare @xml1 XML=
'<data>
<PersonalInfo>
<Person>
<FirstName>Bob</FirstName>
<LastName>Smith</LastName>
</Person>
<Person>
<FirstName>John</FirstName>
<LastName>Doe</LastName>
</Person>
</PersonalInfo>
</data>';
EXEC TransformPlainXML @xml1;
declare @xml2 XML=
'<data>
<AddressInfo>
<Address>
<City>Cleveland</City>
<State>OH</State>
</Address>
<Address>
<City>Chicago</City>
<State>IL</State>
</Address>
</AddressInfo>
</data>';
EXEC TransformPlainXML @xml2;
两个结果:
RowIndex FirstName LastName
1 Bob Smith
2 John Doe
和
RowIndex City State
1 Cleveland OH
2 Chicago IL