【问题标题】:How to recursively get XPath of a node using SQL Server?如何使用 SQL Server 递归获取节点的 XPath?
【发布时间】:2015-04-10 17:49:45
【问题描述】:

我已经厌倦了查看可能是我曾经构建过的最丑陋的 SQL 语句,需要您的帮助。我正在 XML 文档中搜索各种元素,并希望查看它们的 XPath。下面的查询通过蛮力运行,但我无法想出一种方法来创建能够正确支持 N 级别的函数或 CTE。

declare @article xml = '<article>
  <front>
    <article-meta>
      <title-group>
        <article-title>Update on ...</article-title>
      </title-group>
    </article-meta>
  </front>
  <back>
    <ref-list>
      <ref id="R1">
        <citation citation-type="journal">
          <article-title>Retrospective study of ...</article-title>
        </citation>
      </ref>
    </ref-list>
  </back>
</article>'

SELECT
        Cast(T.r.query('local-name(parent::*/parent::*/parent::*/parent::*/parent::*/parent::*)') AS varchar(max)) + '/' +
        Cast(T.r.query('local-name(parent::*/parent::*/parent::*/parent::*/parent::*)') AS varchar(max)) + '/' +
        Cast(T.r.query('local-name(parent::*/parent::*/parent::*/parent::*)') AS varchar(max)) + '/' +
        Cast(T.r.query('local-name(parent::*/parent::*/parent::*)') AS varchar(max)) + '/' +
        Cast(T.r.query('local-name(parent::*/parent::*)') AS varchar(max)) + '/' +
        Cast(T.r.query('local-name(parent::*)') AS varchar(max)) AS ThePath,
        Cast(T.r.query('local-name(.)') AS varchar(max)) AS TheElement,
        T.r.query('.') AS TheXml
FROM @article.nodes('//article-title') T(r)

结果:

ThePath                                 TheElement
//article/front/article-meta/title-group    article-title
/article/back/ref-list/ref/citation         article-title

我真正想要的:

SELECT
  x.RowId,
  dbo.GetXPath(T.r.query('.')) AS ThePath, -- <---- Magic function goes here
    T.r.query('.') AS TheXml
FROM dbo.InputFormatXml x
  JOIN dbo.InputFormat f
    ON F.InputFormatId = x.InputFormatId
CROSS APPLY TheData.nodes('//article-title') T(r)
WHERE F.Description = 'NLM';

【问题讨论】:

    标签: sql-server xml xpath sqlxml


    【解决方案1】:
    DECLARE @idoc int;
    
    EXEC sp_xml_preparedocument @idoc OUTPUT, @article; 
    
    SELECT ISNULL(id,'') id, parentid, localname
    INTO #nodetree
    FROM OPENXML(@idoc,'/',3)
    WHERE nodetype = 1;
    
    EXEC sp_xml_removedocument @idoc;
    
    ALTER TABLE #nodetree ADD PRIMARY KEY (id);
    
    WITH cte AS (
      SELECT
        parentid
       ,CAST('/' AS varchar(max)) + localname AS xpath
      FROM #nodetree WHERE localname = 'article-title'
      UNION ALL
      SELECT
        parent.parentid
       ,CAST('/' AS varchar(max)) + localname + xpath
      FROM cte AS node
      INNER JOIN #nodetree parent on parent.id = node.parentid
    )
    SELECT xpath
    FROM cte
    WHERE parentid IS NULL
    

    【讨论】:

      【解决方案2】:

      向下而不是向上递归:

      WITH cte AS (
        SELECT
          node  = x.query('.')
         ,name  = x.value('local-name(.)','varchar(max)')
         ,xpath = CAST('' AS varchar(max))
        FROM (SELECT @article AS node) parent
        CROSS APPLY node.nodes('/*') T(x)
        UNION ALL
        SELECT
          node  = x.query('.')
         ,name  = x.value('local-name(.)','varchar(max)')
         ,xpath = parent.xpath + '/' + parent.name
        FROM cte parent
        CROSS APPLY node.nodes('/*/*') T(x)
      )
      SELECT 
        xpath
       ,name
      FROM cte
      WHERE name = 'article-title'
      

      【讨论】:

      • 谢谢。这对于我最初列出的简化场景非常有效。我提到了创建一个函数或 CTE,因此我可以在“真实”场景中使用它,在该场景中,我基于 XPath 语句收集了数千个
        元素,并且运行将产生所有父节点的查询需要几个小时,而这不是合理的。在不需要提供数千篇文章的完整数据集的情况下,如何参数化“SELECT @article AS 节点”查询?
      猜你喜欢
      • 2011-01-19
      • 1970-01-01
      • 1970-01-01
      • 2012-06-09
      • 1970-01-01
      • 2017-02-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多