如何使用 SQL 服务器递归获取节点的 XPath?
How to recursively get XPath of a node using SQL Server?
我厌倦了查看可能是我创建过的最丑陋的 SQL 语句,需要您的帮助。我正在 XML 文档中搜索各种元素,并希望查看它们的 XPath。下面的查询通过蛮力工作,但我想不出一种方法来创建一个函数或 CTE 来正确支持 N 级别。
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';
向下递归而不是向上递归:
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'
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
我厌倦了查看可能是我创建过的最丑陋的 SQL 语句,需要您的帮助。我正在 XML 文档中搜索各种元素,并希望查看它们的 XPath。下面的查询通过蛮力工作,但我想不出一种方法来创建一个函数或 CTE 来正确支持 N 级别。
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';
向下递归而不是向上递归:
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'
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