Verwenden Sie XQuery, um einige Kinder herauszufiltern, aber behalten Sie die Eltern bei


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;

Gibt die folgenden Ergebnismengen an

Original:

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

Gefiltert, um Nicht- (b | c) auszuschließen - aber Eltern fehlen:

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

Was ich will (verfügbar mit Löschschritten, aber nicht mit .query):

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

Ist es möglich, die Eltern in einer XQuery zu erhalten?

Antworten:


8

Sie können versuchen, zu folgen

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

Es ist jedoch möglicherweise nicht geeignet, wenn Ihr echtes XML komplexer ist und Sie Knoten auf einer tieferen Verschachtelungsebene ausschließen müssen.


Ehrlich gesagt denke ich nicht, dass XQuery das gute Werkzeug für solche Aufgaben ist. Im Wesentlichen haben wir XML und wir müssen einen Knoten irgendwo innerhalb entfernen, dh wir müssen ihn transformieren . Das geeignetere Werkzeug sind meiner Meinung nach XSL-Transformationen. SQL Server verfügt leider nicht über integrierte XSLT-Funktionen (kann jedoch über die SQLCLR-Funktion hinzugefügt werden).

So könnte XSL zur Lösung dieser Aufgabe aussehen

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

Es funktioniert für aElemente, die sich an einer beliebigen Stelle in XML befinden, nicht nur im Stammverzeichnis.

Die SQLCLR-Funktion zum Durchführen einer nicht parametrisierten XSL-Transformation kann so einfach wie wenige Zeilen C # -Code sein

[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);
}

Es sollte in der Datenbank wie deklariert werden

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

Und es kann dann als verwendet werden

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

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

Vielen Dank, ja, ich habe eine XSLT-Assembly, die ich gelegentlich von Microsoft Master Data Services verwende und die XML-Transformationen durchführt.
Cade Roux

Ich habe es ausprobiert und es funktioniert gut. Wenn sie komplexer werden, muss ich definitiv ein vollständiges XSLT verwenden.
Cade Roux

@CadeRoux, ja, es kommt manchmal vor, dass die Verwendung von .modify () und .query () umständlich oder unmöglich ist, da .modify () jeweils nur einen Knoten verarbeiten kann und sich herausstellt, dass Schleifen nicht die Option sind, weil davon muss in SELECT sein, und es stellt sich heraus, dass XQuery auch nicht die Option ist, da seine Implementierung in SQL Server uralt und begrenzt ist, etc etc ... in solchen Fällen kann XSLT mit besserer Eleganz ins Spiel kommen. Wenn jedoch eine einfache Lösung mit integrierten Funktionen gerade ausreicht, sollten Sie sie nicht verwenden.
i-one
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.