将高度复杂的 XML 结构转换为 table

Converting highly complex XML structure to table

我有一个输出两列的存储过程。一列是 ID,另一列是该 ID 的高度复杂的 XML 格式。我的要求是将此 XML 格式转换为 table 格式。 XML有300个节点和子节点。结构如下:

 <Main>
<Version>1.0</Version>
<CId>459876569</CId>
<Overview>
    <Type>Y</Type>
    <CreateDate>20180505</CreateDate>
    <PlanType>A</PlanType>
    <EffectiveDate>20171201</EffectiveDate>
    <EndDate>20181130</EndDate>
    <Comments>No other comments</Comments>
</Overview>
<EssentialInfo>
    <ContactI>
        <LastName>Doe</LastName>
        <MiddleName>A</MiddleName>
        <FirstName>John</FirstName>
        <DateOfBirth>19500808</DateOfBirth>
        <Gender>F</Gender>
        <Address>
            <AddressLine1>dfsfsdf</AddressLine1>
            <AddressLine2>dsfsdfa</AddressLine2>
            <City>gdfgdfg</City>
        </Address>
        <HomePhone>98745632148</HomePhone>
    </Contact>
  </EssentialInfo>
</Main>

我知道 OPENXML 方法,但命名 300 列使它变得乏味。还有其他方法可以解决这个问题吗?

我正在尝试实现 Workflow suggestion - XML & SQL 此功能,为此我正在尝试将我的巨大 XML 转换为 SQL table

如果将 TVF 作为辅助函数打开,并假设 XML 将转为一条记录。

显然 完全声明的 SQL 会更高效。

例子

Declare @XML xml ='
<Main>
<Version>1.0</Version>
<CId>459876569</CId>
<Overview>
    <Type>Y</Type>
    <CreateDate>20180505</CreateDate>
    <PlanType>A</PlanType>
    <EffectiveDate>20171201</EffectiveDate>
    <EndDate>20181130</EndDate>
    <Comments>No other comments</Comments>
</Overview>
<EssentialInfo>
    <Contact>
        <LastName>Doe</LastName>
        <MiddleName>A</MiddleName>
        <FirstName>John</FirstName>
        <DateOfBirth>19500808</DateOfBirth>
        <Gender>F</Gender>
        <Address>
            <AddressLine1>dfsfsdf</AddressLine1>
            <AddressLine2>dsfsdfa</AddressLine2>
            <City>gdfgdfg</City>
        </Address>
        <HomePhone>98745632148</HomePhone>
    </Contact>
  </EssentialInfo>
</Main>
'

Select * Into #Temp from [dbo].[tvf-XML-Hier](@XML) Order by R1

Declare @SQL varchar(max) = '
Select *
 From (
        Select Item = concat(Element,IIF(Attribute='''','''',''_''+Attribute))
              ,Value
         From #Temp
      ) A
 Pivot (max([Value]) For [Item] in (' + Stuff((Select ','+QuoteName(concat(Element,IIF(Attribute='','','_'+Attribute))) 
                                                From  #Temp 
                                                Where Value is not null 
                                                Order by R1
                                                For XML Path('')),1,1,'')  + ') ) p'
Exec(@SQL);

Returns

感兴趣的 TVF

ALTER FUNCTION [dbo].[tvf-XML-Hier](@XML xml)

Returns Table 
As Return

with  cte0 as ( 
                  Select Lvl       = 1
                        ,ID        = Cast(1 as int) 
                        ,Pt        = Cast(NULL as int)
                        ,Element   = x.value('local-name(.)','varchar(150)')
                        ,Attribute = cast('' as varchar(150))
                        ,Value     = x.value('text()[1]','varchar(max)')
                        ,XPath     = cast(concat(x.value('local-name(.)','varchar(max)'),'[' ,cast(Row_Number() Over(Order By (Select 1)) as int),']') as varchar(max))
                        ,Seq       = cast(1000000+Row_Number() over(Order By (Select 1)) as varchar(max))
                        ,AttData   = x.query('.') 
                        ,XMLData   = x.query('*') 
                  From   @XML.nodes('/*') a(x) 
                  Union  All
                  Select Lvl       = p.Lvl + 1 
                        ,ID        = Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10
                        ,Pt        = p.ID
                        ,Element   = c.value('local-name(.)','varchar(150)')
                        ,Attribute = cast('' as varchar(150))
                        ,Value     = cast( c.value('text()[1]','varchar(max)') as varchar(max) ) 
                        ,XPath     = cast(concat(p.XPath,'/',c.value('local-name(.)','varchar(max)'),'[',cast(Row_Number() Over(PARTITION BY c.value('local-name(.)','varchar(max)') Order By (Select 1)) as int),']') as varchar(max) )
                        ,Seq       = cast(concat(p.Seq,' ',10000000+Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10) as varchar(max))
                        ,AttData   = c.query('.') 
                        ,XMLData   = c.query('*') 
                  From   cte0 p 
                  Cross  Apply p.XMLData.nodes('*') b(c) 
              )
    , cte1 as (   
                  Select R1 = Row_Number() over (Order By Seq),A.*
                  From  (
                          Select  Lvl,ID,Pt,Element,Attribute,Value,XPath,Seq From cte0
                          Union All
                          Select Lvl       = p.Lvl+1
                                ,ID        = p.ID + Row_Number() over (Order By (Select NULL)) 
                                ,Pt        = p.ID
                                ,Element   = p.Element
                                ,Attribute = x.value('local-name(.)','varchar(150)')
                                ,Value     = x.value('.','varchar(max)')
                                ,XPath     = p.XPath + '/@' + x.value('local-name(.)','varchar(max)')
                                ,Seq       = cast(concat(p.Seq,' ',10000000+p.ID + Row_Number() over (Order By (Select NULL)) ) as varchar(max))
                          From   cte0 p 
                          Cross  Apply AttData.nodes('/*/@*') a(x) 
                        ) A 
               )

Select A.R1
      ,R2  = IsNull((Select max(R1) From cte1 Where Seq Like A.Seq+'%'),A.R1)
      ,A.Lvl
      ,A.ID
      ,A.Pt
      ,A.Element
      ,A.Attribute
      ,A.XPath
      ,Title = Replicate('|---',Lvl-1)+Element+IIF(Attribute='','','@'+Attribute)
      ,A.Value
 From  cte1 A

/*
Source: http://beyondrelational.com/modules/2/blogs/28/posts/10495/xquery-lab-58-select-from-xml.aspx

Declare @XML xml='<person><firstname preferred="Annie" nickname="BeBe">Annabelle</firstname><lastname>Smith</lastname></person>'
Select * from [dbo].[tvf-XML-Hier](@XML) Order by R1
*/

如果对可视化有帮助,TVF returns

Clearly this can be thinned down if you don't need all the columns