如何切碎传递到存储过程中的XML?

时间:2018-09-06 17:48:50

标签: sql-server alibaba-cloud-rds

我将XML传递给以下格式的存储过程。我正在努力在SQL Server 2017中将其切碎:

declare @xml xml =  convert(xml, N'<SearchQuery>
    <DealTypeDesc>Deal</DealTypeDesc>
    <VendorNum>1</VendorNum>
    <VendorName>Vendor1</VendorName>
    <VendorNum>2</VendorNum>
    <VendorName>Vendor2</VendorName>
    <VendorNum>3</VendorNum>
    <VendorName>Vendor3</VendorName>
    <VendorNum>4</VendorNum>
    <VendorName>Vendor4</VendorName>
    <VendorNum>5</VendorNum>
    <VendorName>Vendor5</VendorName>
</SearchQuery>')

-- this is how it is being consumed now.  1 element at a time
SELECT 
    t.c.value('text()[1]', 'NVARCHAR(MAX)') AS LookupValue 
FROM 
    @xml.nodes('//SearchQuery/VendorNum') AS t(c)

-- I want the results to look like this where I can use the node name as a column and the value as a lookup.  
-- I wanted to do this in one pass without having to union all and select from the XML variable every time.
-- I can't change the XML structure since it's coming from a third party, so that option is out.
SELECT 
    'VendorName' ColumnName, 
    t.c.value('text()[1]', 'NVARCHAR(MAX)') AS LookupValue 
FROM 
    @xml.nodes('//SearchQuery/VendorName') AS t(c)

UNION ALL

SELECT 
    'VendorNum' ColumnName, 
    t.c.value('text()[1]', 'NVARCHAR(MAX)') AS LookupValue 
FROM 
    @xml.nodes('//SearchQuery/VendorNum') AS t(c)

2 个答案:

答案 0 :(得分:2)

这里有两个选项。第一个是简单的查询。第二个是辅助函数,它将切碎几乎所有XML,而无需了解任何有关XML的信息。我会经常在发现阶段使用它。

第一个选项

Select ColumnName  = a.value('local-name(.)','varchar(100)')
      ,LookupValue = a.value('.','varchar(max)') 
 From  @XML.nodes('SearchQuery')  as C1(n)
 Cross Apply C1.n.nodes('*') as C2(a)
 Where a.value('local-name(.)','varchar(100)') like 'Vendor%'

返回

ColumnName  LookupValue
VendorNum   1
VendorName  Vendor1
VendorNum   2
VendorName  Vendor2
VendorNum   3
VendorName  Vendor3
VendorNum   4
VendorName  Vendor4
VendorNum   5
VendorName  Vendor5

第二种选择

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

返回

也许比您想要的更多,但易于修剪

enter image description here

感兴趣的TVF

CREATE 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
*/

答案 1 :(得分:0)

您可以分别查询两列,然后按以下方式加入:

select VendorNum, VendorName
from
(
    SELECT 
        n = ROW_NUMBER() OVER (Order by t.vendor.value('text()[1]','NVARCHAR(MAX)')),
        VendorNum= t.vendor.value('text()[1]','NVARCHAR(MAX)')
    FROM @xml.nodes('SearchQuery/VendorNum') AS t(vendor)
) N
join
(
    SELECT 
        n = ROW_NUMBER() OVER (Order by t.vendor.value('text()[1]','NVARCHAR(MAX)')),
        VendorName = t.vendor.value('text()[1]','NVARCHAR(MAX)') 
    FROM @xml.nodes('SearchQuery/VendorName') AS t(vendor)
) V on V.n =N.n