it-swarm.com.de

Gibt XML-Sequenzen zurück, in denen ein Attribut kein bestimmtes Zeichen enthält

Betrachten Sie das folgende einfache XML:

<xml>
  <customer name="Max">
    <email address="[email protected]" />
  </customer>
  <customer name="Erik">
    <email address="[email protected]" />
  </customer>
  <customer name="Brent">
    <email address="brentcom" />
  </customer>
</xml>

Ich möchte eine Liste von <Customer> - Sequenzen erhalten, bei denen das Attribut address des Elements <email> nicht enthält einen @.

Ich möchte also eine Ausgabe, die wie folgt aussieht:

<customer name="Brent">
  <email address="brentcom" />
</customer>

mcve :

DECLARE @x XML = '<xml>
<customer name="Max"><email address="[email protected]" /></customer>
<customer name="Erik"><email address="[email protected]" /></customer>
<customer name="Brent"><email address="brentcom" /></customer>
</xml>';

Diese Abfrage:

SELECT WithValidEmail = @x.query('/xml/customer/email[contains(@address, "@")]')
    , WithInvalidEmail = @x.query('/xml/customer/email[contains(@address, "@")] = False');

Kehrt zurück:

╔═══════════════════════════════════════╦══════════════════╗
║            WithValidEmail             ║ WithInvalidEmail ║
╠═══════════════════════════════════════╬══════════════════╣
║ <email address="[email protected]" />        ║                  ║
║ <email address="[email protected]" /> ║ false            ║
╚═══════════════════════════════════════╩══════════════════╝

Diese Abfrage:

SELECT WithInValidEmail = @x.query('/xml/customer/email')
WHERE @x.exist('/xml/customer/email[contains(@address, "@")]') = 0;

Kehrt zurück:

╔══════════════════╗
║ WithInValidEmail ║
╚══════════════════╝
    (no results)

Die WHERE -Klausel in der obigen Abfrage eliminiert den gesamten XML-Satz, da mindestens eine einzelne Sequenz vorhanden ist, in der die E-Mail-Adresse ein "@" - Zeichen enthält.

10
Max Vernon

Eine einfache Möglichkeit, dies zu tun, besteht darin, die nodesMethode zu verwenden, um direkt zum Attribut address zu gelangen und nach Ihrem @ - Zeichen zu suchen.

Das Problem mit der Art und Weise, wie Sie jetzt suchen, ist, dass nur überprüft wird, ob eine E-Mail-Adresse einen @ Enthält. Durch Parsen der XML-Knoten können Sie einzelne E-Mails darauf überprüfen.

DECLARE @x XML
    = '<xml>
<customer name="Max"><email address="[email protected]" /></customer>
<customer name="Erik"><email address="[email protected]" /></customer>
<customer name="Brent"><email address="brentcom" /></customer>
</xml>';


SELECT x.c.value('@address', 'VARCHAR(100)') AS [email]
FROM   @x.nodes('/xml/customer/email') AS x(c)
WHERE  x.c.exist('@address[contains(., "@")]') = 0;

Wenn Sie eine tatsächliche Tabelle mit einer XML-Spalte wie dieser abfragen müssen, müssen Sie nur die Knotenmethode wie folgt CROSS APPLY:

SELECT x.c.value('@address', 'VARCHAR(100)') AS [email]
FROM @x_table AS xt
CROSS APPLY xt.x.nodes('/xml/customer/email') AS x(c)
WHERE  x.c.exist('@address[contains(., "@")]') = 0;

Wenn Sie das gesamte XML <customer>...</customer> Für diese "Zeile" zurückbringen möchten, können Sie die Achse zurückgehen. Beachten Sie jedoch, dass das Zurückgehen die Leistung großer XML-Blöcke etwas beeinträchtigen kann.

SELECT x.c.query('..')
FROM @x_table AS xt
CROSS APPLY xt.x.nodes('/xml/customer/email') AS x(c)
WHERE  x.c.exist('@address[contains(., "@")]') = 0;

Eine andere Möglichkeit ist:

SELECT @x.query('/xml/customer[email/@address[not(contains(., "@"))]]') answer

Wenn Sie die eckigen Klammern verschieben, um den E-Mail-Knoten zu umschließen, wird die Klausel WHERE effektiv auf den Knoten customer angewendet. Die Übersetzung dieser XQuery ins Englische sieht folgendermaßen aus:

Holen Sie sich alle xml/customer Knoten mit einem email Knoten, der ein address Attribut hat, das das Symbol @ Nicht enthält

11
Erik Darling

Du warst so nah dran. Sie waren definitiv auf dem richtigen Weg, wenn Sie die Funktion .query() und die Funktion contains XQuery verwenden. Was Sie falsch verstanden haben war:

  1. Setzen des = Falseaußerhalb des [...] (Das heißt, es war nicht Teil des Ausdrucks contains())
  2. Verwenden des Wortes False anstelle der Funktion false()
  3. Keine Angabe des übergeordneten Knotens durch Hinzufügen von /.. Am Ende des Pfads (sodass das Ergebnis das Element <customer> Und nicht nur das Element <email> Enthält).

Wenn Sie diese drei Dinge korrigieren, erhalten Sie den folgenden XQuery-Ausdruck, mit dem Sie das bekommen, was Sie wollen:

'/xml/customer/email[contains(@address, "@") = false()]/..'

Wenn Sie dies aus der Frage in Ihr ursprüngliches Beispiel einfügen, erhalten Sie:

DECLARE @x XML = '<xml>
<customer name="Max"><email address="[email protected]" /></customer>
<customer name="Erik"><email address="[email protected]" /></customer>
<customer name="Brent"><email address="brentcom" /></customer>
</xml>';

SELECT
@x.query('/xml/customer/email[contains(@address, "@")]/..') AS [WithValidEmail],
@x.query('/xml/customer/email[contains(@address, "@")=false()]/..') AS [WithInvalidEmail;

Diese Abfrage gibt die folgende Ergebnismenge einer einzelnen Zeile mit zwei XML-Feldern zurück:

WithValidEmail                            |     WithInvalidEmail
<customer name="Max">                     |     <customer name="Brent">
  <email address="[email protected]" />          |       <email address="brentcom" />
</customer>                               |     </customer>
<customer name="Erik">                    |
  <email address="[email protected]" />   |
</customer>                               |

Dies ist wahrscheinlich effizienter als das Ausbrechen des Dokuments mit der Funktion .nodes(), da es das XML in einem einzigen Schuss analysieren kann und nicht den Parser für jeden Knoten starten und stoppen muss.

Der andere Vorteil der Beibehaltung von .query() besteht darin, dass Sie ein einzelnes XML-Dokument zurückerhalten. Wenn Sie also ein XML-Dokument/einen XML-Wert erhalten, der mehrere Knoten enthält, können Sie den skalaren Wertansatz beibehalten, dass es sich um eine einzelne Entität handelt, ohne die resultierenden Knoten erneut in ein Dokument rekonstruieren zu müssen. Auf diese Weise können Sie es auch in einer Unterabfrage/einem CTE verwenden, ohne die Anzahl der erwarteten zurückgegebenen Zeilen zu ändern.

4
Solomon Rutzky