2017-01-10 178 views
2

我需要從各種元素的長列表中提取所有屬性。因此,我正在試圖構建一個循環,遍歷所有元素並返回它們的屬性。提取元素屬性的循環XML

通過幾個帖子,我已經能夠寫下面的代碼。然而,我有超過1000個元素,爲什麼我會非常高興,如果它有可能圍繞後者建立一個循環而不是複製所有元素。

with cte as 
(
select cast(
'<schema fwRel="2"> 
<taxFormId isPrimeKey="true" fkRef="C1-TXFRM" mapField="TAX_FORM_ID" dataType="string"/> 
<formType fkRef="C1-FRMTY" mapField="FORM_TYPE_CD" dataType="string"/> 
<bo suppress="true" required="true" fkRef="F1-BUSOB" mapField="BUS_OBJ_CD" dataType="string"/> 
<transferReason mdField="C1_TXF_TFRRSN_FLG" dataType="lookup" mapXML="BO_DATA_AREA" lookup="C1_TXF_TFRRSN_FLG"/> 
</schema>' as xml) xml_col 
) 

select cte.xml_col.value('(/schema/taxFormId/@fkRef)[1]', 'varchar(100)') as Dummy1 
cte.xml_col.value('(/schema/taxFormId/@mapField)[1]', 'varchar(100)') as Dummy2 
cte.xml_col.value('(/schema/taxFormId/@dataType)[1]', 'varchar(100)') as Dummy3 
cte.xml_col.value('(/schema/taxFormId/@mapXML)[1]', 'varchar(100)') as Dummy4 

from cte 

我希望我已經提供了足夠的信息

+0

您提到的代碼 - 你可以添加它嗎? – Damian

+0

不知怎麼,邊緣把我搞砸了。現已附上 –

回答

1

一個簡單的方法來獲取屬性上市:

with cte as 
(
select cast(
'<schema fwRel="2"> 
    <taxFormId isPrimeKey="true" fkRef="C1-TXFRM" mapField="TAX_FORM_ID" dataType="string" /> 
    <formType fkRef="C1-FRMTY" mapField="FORM_TYPE_CD" dataType="string" /> 
    <bo suppress="true" required="true" fkRef="F1-BUSOB" mapField="BUS_OBJ_CD" dataType="string" /> 
    <transferReason mdField="C1_TXF_TFRRSN_FLG" dataType="lookup" mapXML="BO_DATA_AREA" lookup="C1_TXF_TFRRSN_FLG" /> 
</schema>' as xml) xml_col 
) 

- 查詢使用.nodes(N'/schema/*')列出以下<schema>.nodes(N'@*')所有節點列出所有屬性此節點中:

select nd.value(N'local-name(.)',N'nvarchar(max)') AS NodeName 
     ,attr.value(N'local-name(.)',N'nvarchar(max)') AS AttrName 
     ,attr.value(N'.',N'nvarchar(max)') AS AttrValue 
from cte 
OUTER APPLY xml_col.nodes(N'/schema/*') AS A(nd) 
OUTER APPLY A.nd.nodes(N'@*') AS B(attr) 

結果:

taxFormId  isPrimeKey true 
taxFormId  fkRef  C1-TXFRM 
taxFormId  mapField TAX_FORM_ID 
taxFormId  dataType string 
formType  fkRef  C1-FRMTY 
formType  mapField FORM_TYPE_CD 
formType  dataType string 
bo    suppress true 
bo    required true 
bo    fkRef  F1-BUSOB 
bo    mapField BUS_OBJ_CD 
bo    dataType string 
transferReason mdField  C1_TXF_TFRRSN_FLG 
transferReason dataType lookup 
transferReason mapXML  BO_DATA_AREA 
transferReason lookup  C1_TXF_TFRRSN_FLG 

如果你需要像在您的示例聲明一個可以動態創建(作爲字符串)並使用EXEC來執行此操作(動態SQL)。

+0

謝謝!這節省了我的一天:) –

0

這可能是有點過殺人,但我經常使用TVF來解析和散列大型XML文件。該函數將返回具有範圍鍵R1/R2的父/子層次結構中的數據。

例如:

Declare @XML xml=' 
<schema fwRel="2"> 
    <taxFormId isPrimeKey="true" fkRef="C1-TXFRM" mapField="TAX_FORM_ID" dataType="string"/> 
    <formType fkRef="C1-FRMTY" mapField="FORM_TYPE_CD" dataType="string"/> 
    <bo suppress="true" required="true" fkRef="F1-BUSOB" mapField="BUS_OBJ_CD" dataType="string"/> 
    <transferReason mdField="C1_TXF_TFRRSN_FLG" dataType="lookup" mapXML="BO_DATA_AREA" lookup="C1_TXF_TFRRSN_FLG"/> 
</schema>' 

Select * from [dbo].[udf-XML-Hier](@XML) Order by R1 

返回

enter image description here

你會注意到,對於元素名稱欄,屬性名,XPath的,標題(可選),和價值。 R1,R2,Lv1,ID和PT都是派生的。

作爲一個TVF,你可以應用任何WHEREORDER所需


的UDF(與原始來源)如果有興趣

CREATE FUNCTION [dbo].[udf-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(10000001 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].[udf-XML-Hier](@XML) Order by R1 
*/ 
+0

謝謝! :)我設法得到這個工作以及。但是,正如你自己所說,這只是一個小小的矯枉過正。 –