SET NOCOUNT ON;
DECLARE @xml AS Xml = '<a><b>bbb</b><c>ccc</c><d>ddd</d></a>';
SELECT @xml;
SELECT @xml.query('/a/*[self::b or self::c]');
SET @xml.modify('delete /a/d');
SELECT @xml;
Run Code Online (Sandbox Code Playgroud)
给出以下结果集
原来的:
<a><b>bbb</b><c>ccc</c><d>ddd</d></a>
Run Code Online (Sandbox Code Playgroud)
过滤以排除非 (b|c) - 但缺少父母:
<b>bbb</b><c>ccc</c>
Run Code Online (Sandbox Code Playgroud)
我想要什么(可用于删除步骤,但不可用于 .query):
<a><b>bbb</b><c>ccc</c></a>
Run Code Online (Sandbox Code Playgroud)
是否可以在 XQuery 中保留父项?
您可以尝试以下
SELECT @xml.query('
element a {
for $node in /a/*[local-name() != "d"]
return $node
}');
Run Code Online (Sandbox Code Playgroud)
但是如果您的真实 XML 更复杂并且您必须在更深的嵌套级别排除节点,则它可能不合适。
老实说,我不认为 XQuery 是处理此类任务的好工具。本质上,我们有 XML,我们需要在其中的某个地方删除一个节点,即我们需要对其进行转换。我认为更合适的工具是 XSL 转换。不幸的是,SQL Server 没有内置的 XSLT 功能(不过可以通过 SQLCLR 函数添加)。
这就是用于解决此任务的 XSL 正文的外观
<!-- Skip "d" under "a" -->
<xsl:template match="a/d" />
<!-- Apply identity transform to other nodes -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
Run Code Online (Sandbox Code Playgroud)
它适用于a驻留在 XML 中任何位置的元素,而不仅仅是在根中。
用于执行非参数化 XSL 转换的 SQLCLR 函数可以像几行 C# 代码一样简单
[SqlFunction(Name = "XslTransform")]
public static SqlXml XslTransform(SqlXml xml, SqlXml xsl)
{
if (xml.IsNull || xsl.IsNull)
return SqlXml.Null;
var xslt = new XslCompiledTransform();
using (var xr = xsl.CreateReader())
xslt.Load(xr);
var xws = new XmlWriterSettings
{
Encoding = Encoding.Unicode,
OmitXmlDeclaration = true
};
var output = new MemoryStream();
using (var xw = XmlWriter.Create(output, xws))
using (var xr = xml.CreateReader())
{
xslt.Transform(xr, null, xw);
xw.Flush();
}
output.Seek(0, SeekOrigin.Begin);
return new SqlXml(output);
}
Run Code Online (Sandbox Code Playgroud)
它应该在数据库中声明,如
CREATE FUNCTION SQLCLR.XslTransform
(
@xml xml,
@xsl xml
)
RETURNS xml
AS EXTERNAL NAME [AssemblyName].[ClassName].[XslTransform];
GO
Run Code Online (Sandbox Code Playgroud)
然后它可以用作
DECLARE
@xml xml = N'(Your XML goes here)',
@xsl xml = N'(Your XSL goes here)';
SELECT SQLCLR.XslTransform(@xml, @xsl);
Run Code Online (Sandbox Code Playgroud)