Sử dụng XQuery để lọc một số trẻ em, nhưng bảo vệ cha mẹ


8
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;

Cung cấp các kết quả sau

Nguyên:

<a><b>bbb</b><c>ccc</c><d>ddd</d></a>

Đã lọc để loại trừ non- (b | c) - nhưng cha mẹ bị thiếu:

<b>bbb</b><c>ccc</c>

Những gì tôi muốn (có sẵn với các bước xóa nhưng không phải với .query):

<a><b>bbb</b><c>ccc</c></a>

Có thể bảo tồn cha mẹ trong XQuery không?

Câu trả lời:


8

Bạn có thể thử làm theo

SELECT @xml.query('
    element a {
        for $node in /a/*[local-name() != "d"]
            return $node
    }');

Nhưng nó có thể không phù hợp nếu XML thực của bạn phức tạp hơn và bạn phải loại trừ nút ở mức lồng nhau sâu hơn.


Nói một cách trung thực, tôi không nghĩ rằng XQuery là công cụ tốt cho các loại nhiệm vụ như vậy. Về bản chất, chúng ta có XML và chúng ta cần loại bỏ một nút ở đâu đó bên trong, tức là chúng ta cần chuyển đổi nó. Công cụ phù hợp hơn, tôi nghĩ, là các phép biến đổi XSL. Thật không may, SQL Server không có các khả năng XSLT tích hợp (tuy nhiên, nó có thể được thêm thông qua chức năng SQLCLR).

Đây là cách cơ thể của XSL để giải quyết nhiệm vụ này có thể trông như thế nào

<!-- 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>

Nó sẽ hoạt động cho aphần tử nằm ở bất kỳ đâu trong XML, không chỉ ở gốc.

Hàm SQLCLR để thực hiện chuyển đổi XSL không tham số có thể đơn giản như vài dòng mã 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);
}

Nó nên được khai báo trong cơ sở dữ liệu như

CREATE FUNCTION SQLCLR.XslTransform
(
    @xml xml,
    @xsl xml
)
RETURNS xml
AS EXTERNAL NAME [AssemblyName].[ClassName].[XslTransform];
GO

Và nó có thể được sử dụng sau đó như là

DECLARE
    @xml xml = N'(Your XML goes here)',
    @xsl xml = N'(Your XSL goes here)';

SELECT SQLCLR.XslTransform(@xml, @xsl);

Cảm ơn, vâng, tôi có một hội đồng XSLT mà tôi thỉnh thoảng sử dụng từ Microsoft Master Data Services thực hiện các phép biến đổi XML.
Cade Roux

Tôi đã kiểm tra nó và nó hoạt động tốt, nếu chúng phức tạp hơn, tôi chắc chắn sẽ cần sử dụng XSLT đầy đủ.
Cade Roux

@CadeRoux, vâng, đôi khi việc sử dụng .modify () và .query () là khó hiểu hoặc không thể, vì cần phải có trong CHỌN và điều đó cho thấy XQuery cũng không phải là tùy chọn, vì việc triển khai trong SQL Server là cổ xưa và hạn chế, v.v ... trong những trường hợp như vậy XSLT có thể phát huy tác dụng tốt hơn. Nhưng nếu giải pháp đơn giản sử dụng các khả năng tích hợp là vừa đủ, chắc chắn tại sao không sử dụng nó.
i-một
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.