sql 中 openxml 的动态列和记录

Dan*_*eld 2 xml sql-server sql-server-openxml

我有一个非常简单的 xml 文档。唯一的区别是元素可以改变。有一次我可能会:

<data><PersonalInfo>
<Person><FirstName>Bob</FirstName><LastName>Smith</LastName></Person>
<Person><FirstName>John</FirstName><LastName>Doe</LastName></Person>
</PersonalInfo></data>
Run Code Online (Sandbox Code Playgroud)

下次我可能会:

<data><AddressInfo>
<Address><City>Cleveland</City><State>OH</State></Address>
<Address><City>Chicago</City><State>IL</State></Address>
</AddressInfo></data>
Run Code Online (Sandbox Code Playgroud)

我想编写一个 select 语句,根据我目前拥有的 xml 文档生成一个动态表。

例如:对于第一个:

First Name     Last Name
------------------------
Bob             Smith
John            Doe
Etc...
Run Code Online (Sandbox Code Playgroud)

对于第二个

City       State
-----------------------
Cleveland   OH
Chicago     IL
Etc...
Run Code Online (Sandbox Code Playgroud)

这两个例子没有任何关系(鲍勃不是来自克利夫兰,等等......)

我只想使用相同的代码来生成两个表...取决于 xml 文档。当然,唯一的区别是节点引用:

Example 1:  data/PersonalInfo/Person*
Example 2:  data/AddressInfo/Address*
Run Code Online (Sandbox Code Playgroud)

我不想组合或更改 xml 文档结构中的任何内容。它们就是它们所进来的。我如何引用每一个来创建上面的两个不同的表 - 每个进来的 xml 文档都将在一个单独的运行存储过程中。但它将是相同的存储过程。非常感谢任何帮助,提前致谢!

Shn*_*ugo 5

试试这样:

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
Run Code Online (Sandbox Code Playgroud)

并像这样测试它

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;
Run Code Online (Sandbox Code Playgroud)

两个结果:

RowIndex    FirstName   LastName
1           Bob         Smith
2           John        Doe
Run Code Online (Sandbox Code Playgroud)

RowIndex    City        State
1           Cleveland   OH
2           Chicago     IL
Run Code Online (Sandbox Code Playgroud)

  • @DanielRaymondPatfield,[已经在 SQL Server 2005 中]引入了“PIVOT”(https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx)...请注意, “使用 2008 R2”并不意味着您的实际数据库完全作为 2008R2 运行。给定一个从 SS2008R2 中的 BAK 文件打开的旧 SS2000 数据库,它仍然知道它在幕后是 SS2000。1)在主数据库的上下文中运行我的脚本。2) 查看数据库选项并找到实际的兼容性级别,该级别应为 90 或更高。 (2认同)