Überprüfen Sie, ob das XML-Element auf einer beliebigen Ebene im Dokument mit einem bestimmten Wert vorhanden ist

7

Ist es möglich, XML abzufragen, um festzustellen, ob ein bestimmtes Element einen bestimmten Wert hat? Zum Beispiel, wenn ich sehen wollte, ob das folgende XML den Wert "Brandt" in hat <ContactFName>.

Beachten Sie jedoch, dass sich die Position des Elements ändern kann. In einigen Fällen kann es sein /root/MCTLocations/MCTLocation, oder es kann unter die Wurzel springen oder woanders erscheinen ...

Und ist es möglich, den Elementnamen zu parametrisieren?

DECLARE @table TABLE (XmlCol XML)

INSERT INTO @table (XmlCol) VALUES ('
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
    <MCTLocation>
        <Address>1234 Main Street</Address>
        <ContactFName>Chris</ContactFName>
        <ContactLName>Brandt</ContactLName>
    </MCTLocation>
</MCTLocations>
</root>')

SELECT * FROM @table WHERE ??
John Hennesey
quelle

Antworten:

13

Zu diesem Zweck möchten Sie die .exist()XML-Funktion verwenden, da sie einen BIT-Wert (dh einen booleschen Wert) zurückgibt, der angibt, ob die XQuery etwas findet oder nicht.

Um die nicht statische Position eines Elements zu behandeln, verwenden Sie entweder *(zeigt an, dass alle Knoten einer bestimmten Ebene überprüft werden sollen, jedoch nicht andere Ebenen) oder //(zeigt an, dass alle Knoten auf dieser Ebene und darunter überprüft werden sollen).

In den folgenden Beispielen wird die Beispielabfrage aus der Frage als Basis verwendet, und es werden einige Testfälle hinzugefügt, um das Element auf verschiedenen Ebenen zu platzieren. Außerdem wird ein Testfall hinzugefügt, der den Namen ändert, um zu zeigen, dass die XQuery nicht nur alles auswählt.

Test Setup (einmal ausführen)

SET NOCOUNT ON;
CREATE TABLE #Table (ID INT NOT NULL, XmlCol XML);

INSERT INTO #Table (ID, XmlCol) VALUES (1, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
    <MCTLocation>
        <Address>1234 Main Street</Address>
        <ContactFName>Chris</ContactFName>
        <ContactLName>Brandt</ContactLName>
    </MCTLocation>
</MCTLocations>
</root>');

INSERT INTO #Table (ID, XmlCol) VALUES (2, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
    <MCTLocation>
        <Address>1234 Main Street</Address>
        <ContactFName>Chris</ContactFName>
        <ContactLName>Grandt</ContactLName>
    </MCTLocation>
</MCTLocations>
</root>');

INSERT INTO #Table (ID, XmlCol) VALUES (3, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
    <MCTLocation>
        <Address>1234 Main Street</Address>
        <ContactFName>Chris</ContactFName>
    </MCTLocation>
</MCTLocations>
<ContactLName>Brandt</ContactLName>
</root>');

INSERT INTO #Table (ID, XmlCol) VALUES (4, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
    <MCTLocation>
        <Address>1234 Main Street</Address>
        <ContactFName>Chris</ContactFName>
    </MCTLocation>
    <NewElement>
       <SubElement>
          <ContactLName>Brandt</ContactLName>
       </SubElement>
    </NewElement>
</MCTLocations>
</root>');

INSERT INTO #Table (ID, XmlCol) VALUES (5, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
    <MCTLocation>
        <Address>1234 Main Street</Address>
        <ContactFName>Chris</ContactFName>
    </MCTLocation>
    <NewerElement>
       <ContactLName>Brandt</ContactLName>
    </NewerElement>
</MCTLocations>
</root>');

INSERT INTO #Table (ID, XmlCol) VALUES (6, N'
<root>
<MCTClientName>John</MCTClientName>
<MCTClientCity>Palm Beach</MCTClientCity>
<MCTLocations>
    <MCTLocation>
        <Address>1234 Main Street</Address>
        <ContactFName>Chris</ContactFName>
    </MCTLocation>
    <NewerElement>
    </NewerElement>
</MCTLocations>
</root>
<ContactLName>Brandt</ContactLName>
');

Test 1 ( *anstelle eines Knotennamens)

Dadurch werden alle Knoten auf der angegebenen Ebene überprüft, die in diesem Fall knapp darunter liegt <root>. Andere Ebenen werden jedoch nicht überprüft.

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'/*/ContactLName[text()="Brandt"]') = 1;

Gibt eine Zeile mit dem IDWert 3 zurück.

Test 2 ( *anstelle eines Knotennamens)

Dadurch werden alle Knoten auf der angegebenen Ebene überprüft, die in diesem Fall knapp darunter liegt <root><MCTLocations>. Andere Ebenen werden jedoch nicht überprüft.

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'/root/MCTLocations/*/ContactLName[text()="Brandt"]') = 1;

Gibt Zeilen mit den IDWerten 1 und 5 zurück.

Test 3 ( //anstelle eines Knotennamens)

Dadurch werden alle Knoten überprüfen beginnend auf der angegebenen Ebene, die in diesem Fall ist knapp <root><MCTLocations>, und unten .

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'/root/MCTLocations//ContactLName[text()="Brandt"]') = 1;

Gibt Zeilen mit den IDWerten 1, 4 und 5 zurück.

Test 4 ( /*oder */anstelle eines Knotennamens)

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'*//ContactLName[text()="Brandt"]') = 1;

-- and:

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'//*/ContactLName[text()="Brandt"]') = 1;

Beide geben Zeilen mit den IDWerten 1, 3, 4 und 5 zurück.

Diese geben keine Zeilen-ID von 6 zurück, da *sie ein Platzhalter für einen einzelnen Knoten sind, daher würde die höchste zulässige Ebene unter <root>(oder einem Knoten der obersten Ebene) liegen.

Test 5 ( //auf der obersten Ebene)

Dadurch werden alle Knoten überprüft, die auf der obersten Ebene beginnen.

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'//ContactLName[text()="Brandt"]') = 1;

Gibt Zeilen mit den IDWerten 1, 3, 4, 5 und 6 zurück.

Test 6 (lokalen Variablenwert für Elementtext in XQuery verwenden)

DECLARE @Name NVARCHAR(50) = N'Brandt';

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'//ContactLName[text()=sql:variable("@Name")]') = 1;

SET @Name = N'Grandt';

-- exact same query, just different value in the variable
SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'//ContactLName[text()=sql:variable("@Name")]') = 1;

Die erste Abfrage gibt Zeilen mit den IDWerten 1, 3, 4, 5 und 6 zurück.

Die zweite Abfrage gibt eine Zeile mit dem IDWert 2 zurück.

Test 7 (Funktion und Zeichenfolgenliteral für Elementnamen in XQuery verwenden)

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'//.[local-name()="NewerElement"]') = 1;

Gibt Zeilen mit den IDWerten 5 und 6 zurück.

Test 8 (Funktion mit lokalem Variablenwert für Elementnamen in XQuery verwenden)

DECLARE @Node NVARCHAR(50) = N'SubElement';

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'//.[local-name()=sql:variable("@Node")]') = 1;

Gibt eine Zeile mit dem IDWert 4 zurück.

Test 9 (alle Teile zusammenfügen)

DECLARE @NodeName NVARCHAR(50) = N'ContactLName',
        @NodeText NVARCHAR(500) = N'Brandt';

SELECT *
FROM   #Table tmp
WHERE  tmp.[XmlCol].exist(N'//.[local-name()=sql:variable("@NodeName")]
   [text()=sql:variable("@NodeText")]') = 1;

Gibt Zeilen mit den IDWerten 1, 3, 4, 5 und 6 zurück.


Allgemeiner XML-Hinweis:

XML-Daten (in SQL Server) werden wie NVARCHAR/ als UTF-16 Little Endian codiert NCHAR. Daher ist es am besten, Sting-Literalen ein Großbuchstaben voranzustellen - Nwenn der Wert wirklich XML ist.

Solomon Rutzky
quelle
1
Das ist tolles Zeug! Vielen Dank!! Eine letzte Frage: Ist es möglich, den Elementnamen zu parametrisieren?
John Hennesey
3
Dies ist bei weitem die beste Reihe von Beispielen, die ich in einer Antwort erhalten habe. Ich danke dir sehr!
John Hennesey
2
@ JohnHennesey Gern geschehen. Und danke für das Kompliment :-).
Solomon Rutzky