【问题标题】:SQL Variable Containing Path in XML NodeXML 节点中包含路径的 SQL 变量
【发布时间】:2016-08-26 11:12:13
【问题描述】:

在将 XML 路径通过变量传递给 nodes() 方法时,我需要帮助。我查看了几个不同的帖子,发现可以使用local-namesql:variable 传递一个节点。下面的示例按预期工作:

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)

但是,当我尝试传递路径而不是单个节点时,它不起作用:

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)

我需要能够在路径的另一部分之前传递一个始终不变的路径。因此,在我的示例中,“测试”部分始终相同,但其上方的路径会有所不同。

我怀疑local-name 不是这样做的方法。但是有没有其他方法可以做到这一点?

任何帮助将不胜感激。

我曾考虑过使用动态 SQL,但此代码将用于返回结果表的 UDF 中。


编辑:

我相信我不清楚自己要做什么。我需要能够将不同的 XML 路径传递到使用它们来分解 XML 的函数中。

示例 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)

示例 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)

我希望能够将路径传递给函数,以便它可以执行此操作并将其作为表格返回:

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)

但我不知道如何使用路径参数。大多数时候路径会有所不同。

【问题讨论】:

  • 我怀疑——除非通过编写动态 SQL——这是不可能的。
  • 我也是这么想的。我可以在存储过程中使用动态 SQL 来执行此操作,然后使用 INSERT EXEC,但这似乎不是一个很好的解决方案。
  • 不,它不漂亮。但是限制很明确,不能通过变量传递路径。
  • @Tomalak,将变量作为 XPath 传递是不可能的,但我非常感谢通配符在此类问题中的能力:-)

标签: sql xml tsql xml-parsing


【解决方案1】:

您不想更改整个函数,只需更改 XQuery 中的路径。如您所知,这是不可能的。

但是:您似乎在不同的结构中找到了相同的数据。至少您似乎知道,您会在节点 SomeNode 下方找到名为 IDNameValue 的节点...

因此,这种方法可能会以完全不同的方式解决您的问题...它至少适用于您的两个给定示例...

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

在此解决方案中,您的第一个工作示例显示了如何使用 SomeNode 作为变量...

【讨论】:

  • 这似乎有效。当Entry/ExampleItem 有多个Fields/Attributes 时,我只需要进行一些调整,否则XML 解析工作得很好。这真的很有帮助。谢谢!
【解决方案2】:

添加第二个正斜杠以表示深度搜索,并仅在 @XML_Path 变量中查找您所追求的值的父节点:

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)

或者,您可以指定多个级别并返回所有值:

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')

【讨论】:

  • 我认为我没有清楚地解释我想要什么。我已经更新了我的帖子以包含更清晰的解释。感谢您的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 2011-05-19
  • 2019-10-18
相关资源
最近更新 更多