SQL Variable Containing Path in XML Node

风流意气都作罢 提交于 2021-01-28 02:45:49

问题


I need help with passing an XML path through a variable to the nodes() method. I have looked at several different posts and found that a node can be passed by using local-name and sql:variable. The example below works as expected:

DECLARE @XML_Path VARCHAR(MAX) 
      , @XML_In XML


SET @XML_Path = 'GetData'

SET @XML_In = 
'
<GetData>
  <test>234</test>
</GetData>
'

--THIS WORKS
SELECT Item_Idx = Nodes.value('(test)[1]' ,'INT')
FROM   @XML_In.nodes('/*[local-name()=sql:variable("@XML_Path")]') Results(Nodes)

However, it does not work when I try to pass a path instead of a single node:

SET @XML_Path = 'GetData/Hello'

SET @XML_In = 
'
<GetData>
  <Hello>
    <test>234</test>
  </Hello>
</GetData>
'

--THIS DOES NOT WORK
SELECT Item_Idx = Nodes.value('(test)[1]' ,'INT')
FROM   @XML_In.nodes('/*[local-name()=sql:variable("@XML_Path")]') Results(Nodes)

I need to be able to pass a path preceding another part of the path that is always constant. So, in my example, the "test" part would always be the same but the path above it would vary.

I suspect that local-name is not the way to do this. But is there a different way I can do this?

Any help would be appreciated.

I've thought about using dynamic SQL, but this code would be used inside a UDF returning a table of results.


Edit:

I believe I was unclear about what I am trying to do. I need to be able to pass different XML paths into a function that uses them to shred XML.

Example 1

DECLARE @XML_In XML

SELECT @XML_In = 
'
<RootNode1>
  <ExtraNode1>
    <Items>
      <ExampleItem>
        <SomeNode>100</SomeNode>
        <Attributes>
          <ID>1</ID>
          <Name>a</Name>
          <Value>123a</Value>
        </Attributes>
      </ExampleItem>
      <ExampleItem>
        <SomeNode>200</SomeNode>
        <Attributes>
          <ID>2</ID>
          <Name>b</Name>
          <Value>234</Value>
        </Attributes>
      </ExampleItem>
      <ExampleItem>
        <SomeNode>300</SomeNode>
        <Attributes>
          <ID>3</ID>
          <Name>c</Name>
          <Value>345</Value>
        </Attributes>
      </ExampleItem>
    </Items>
  </ExtraNode1>
</RootNode1>
'

    SELECT      SomeNode  = Nodes.value('(../SomeNode)[1]' ,'INT')  
              , ID        = Nodes.value('(ID)[1]'       ,'INT')         
              , Name      = Nodes.value('(Name)[1]'     ,'VARCHAR(100)')     
              , Value     = Nodes.value('(Value)[1]'    ,'NVARCHAR(MAX)')
    FROM        @XML_In.nodes('/RootNode1/ExtraNode1/Items/ExampleItem/Attributes') Results(Nodes)

Example 2

DECLARE @XML_In XML

SELECT @XML_In = 
'
<Example2Root>
  <Entries>
    <Entry>
      <SomeNode>100</SomeNode>
      <Fields>
        <ID>1</ID>
        <Name>a</Name>
        <Value>123a</Value>
      </Fields>
    </Entry>
    <Entry>
      <SomeNode>200</SomeNode>
      <Fields>
        <ID>2</ID>
        <Name>b</Name>
        <Value>234</Value>
      </Fields>
    </Entry>
    <Entry>
      <SomeNode>300</SomeNode>
      <Fields>
        <ID>3</ID>
        <Name>c</Name>
        <Value>345</Value>
      </Fields>
    </Entry>
  </Entries>
</Example2Root>
'

    SELECT      SomeNode  = Nodes.value('(../SomeNode)[1]' ,'INT')  
              , ID        = Nodes.value('(ID)[1]'       ,'INT')         
              , Name      = Nodes.value('(Name)[1]'     ,'VARCHAR(100)')     
              , Value     = Nodes.value('(Value)[1]'    ,'NVARCHAR(MAX)')
    FROM        @XML_In.nodes('/Example2Root/Entries/Entry/Fields') Results(Nodes)

I want to be able to pass the path to the function so it can do this and the return it as a table:

SELECT      SomeNode  = Nodes.value('(../SomeNode)[1]' ,'INT')  
          , ID        = Nodes.value('(ID)[1]'       ,'INT')         
          , Name      = Nodes.value('(Name)[1]'     ,'VARCHAR(100)')     
          , Value     = Nodes.value('(Value)[1]'    ,'NVARCHAR(MAX)')
FROM        @XML_In.nodes('SOME_PATH') Results(Nodes)

But I don't know how to go about using a parameter for the path. The path will be different most times.


回答1:


You do not want to change the whole function, just the path in XQuery. This is - as you know - not possible.

But: It seems that you are finding the same data in differing structures. At least you seem to know, that you will find nodes named ID, Name and Value below a node SomeNode...

So this approach might solve your issue in a completely different way... It works - at least - with your two given examples...

SELECT p.*
FROM
(
    SELECT Nd.value('.','int') AS SomeNode
          ,Deeper.value('local-name(.)','nvarchar(max)') AS NodeName
          ,Deeper.value('.','nvarchar(max)') AS NodeValue
    FROM @XML_In.nodes('//SomeNode') AS Sm(Nd)
    OUTER APPLY Nd.nodes('parent::*/*[local-name(.)!="SomeNode"]/*') AS TwoLevels(Deeper)
) AS tbl
PIVOT
(
    MIN(NodeValue) FOR NodeName IN(ID,Name,Value)
) AS p

In this solution your first working example shows, how you might use SomeNode as variable...




回答2:


Add in a second forward slash to signify a deep search and only look for the parent node of the value you are after in your @XML_Path variable:

DECLARE @XML_Path VARCHAR(MAX) 
      , @XML_In XML

SET @XML_Path = 'Hello'

SET @XML_In = 
'
<GetData>
  <Hello>
    <test>234</test>
    <test>567</test>
  </Hello>
</GetData>
'

SELECT Item_Idx = Nodes.value('(test)[1]' ,'INT')
FROM   @XML_In.nodes('//*[local-name()=sql:variable("@XML_Path")]') Results(Nodes)

SELECT Item_Idx = Nodes.value('(test)[2]' ,'INT')
FROM   @XML_In.nodes('//*[local-name()=sql:variable("@XML_Path")]') Results(Nodes)

Alternatively, you can specify several levels and return all values:

declare @Node varchar(max)
declare @Attribute varchar(max)

set @Attribute = 'World'
set @Node = 'Hello'
select @XML_In.value('(/GetData
                       /*[local-name() = sql:variable("@Node")]
                       /*[local-name() = sql:variable("@Attribute")])[1]',  'int')


来源:https://stackoverflow.com/questions/39164962/sql-variable-containing-path-in-xml-node

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!