Considérez le XML simple suivant:
<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>
Je veux obtenir une liste de <Customer>
séquences où l'attribut address
de <email>
l'élément ne contient pas un @
.
Donc, je veux une sortie qui ressemble à:
<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>';
Cette requête:
SELECT WithValidEmail = @x.query('/xml/customer/email[contains(@address, "@")]')
, WithInvalidEmail = @x.query('/xml/customer/email[contains(@address, "@")] = False');
Retour:
╔═══════════════════════════════════════╦══════════════════╗
║ WithValidEmail ║ WithInvalidEmail ║
╠═══════════════════════════════════════╬══════════════════╣
║ <email address="[email protected]" /> ║ ║
║ <email address="[email protected]" /> ║ false ║
╚═══════════════════════════════════════╩══════════════════╝
Cette requête:
SELECT WithInValidEmail = @x.query('/xml/customer/email')
WHERE @x.exist('/xml/customer/email[contains(@address, "@")]') = 0;
Retour:
╔══════════════════╗
║ WithInValidEmail ║
╚══════════════════╝
(no results)
La clause WHERE
dans la requête ci-dessus élimine l'ensemble complet de XML car au moins une seule séquence existe où l'adresse e-mail contient un signe "@".
Un moyen simple de le faire est d'utiliser la nodes
méthode pour accéder directement à l'attribut address
et vérifier votre @
signe.
Le problème avec la façon dont vous regardez maintenant est qu'il vérifie uniquement que toute adresse e-mail a un @
dedans. L'analyse des nœuds XML vous permet de vérifier les e-mails individuels pour cela.
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;
Si vous devez interroger une table réelle avec une colonne XML comme celle-ci, vous devez simplement CROSS APPLY
la méthode des nœuds comme suit:
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;
Si vous voulez apporter tous les <customer>...</customer>
XML pour cette "ligne" en arrière, vous pouvez reculer l'axe. Sachez simplement que marcher vers l'arrière peut rendre les performances un peu louches pour les gros blocs XML.
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;
Une autre façon de procéder est:
SELECT @x.query('/xml/customer[email/@address[not(contains(., "@"))]]') answer
En déplaçant les crochets pour envelopper le nœud de courrier électronique, la clause WHERE
s'applique effectivement au nœud customer
. La traduction de cette XQuery en anglais ressemble à ceci:
Obtenez-moi tout
xml/customer
nœuds avec un nœudaddress
qui ne contient pas le@
symbole
Tu étais si proche. Vous étiez définitivement sur la bonne voie en utilisant la fonction .query()
et en utilisant la fonction XQuery contains
. Ce que vous vous êtes trompé, c'est:
= False
extérieur du [...]
(Ce qui signifie qu'il ne faisait pas partie de l'expression contains()
)False
au lieu de la fonction false()
/..
À la fin du chemin (de sorte que le résultat inclura l'élément <customer>
Et pas seulement l'élément <email>
)La correction de ces trois choses se traduit par l'expression XQuery suivante qui vous obtient ce que vous voulez:
'/xml/customer/email[contains(@address, "@") = false()]/..'
Mettre cela dans votre exemple original de la question vous donne:
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;
Cette requête renvoie l'ensemble de résultats suivant d'une seule ligne avec deux champs XML:
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> |
C'est probablement plus efficace que de décomposer le document avec la fonction .nodes()
car elle peut analyser le XML en une seule fois et n'a pas besoin de démarrer et d'arrêter l'analyseur pour chaque nœud.
L'autre avantage de le conserver dans .query()
est que vous obtenez un seul document XML retourné. Donc, si vous recevez un document/une valeur XML contenant plusieurs nœuds, vous pouvez conserver l'approche de la valeur scalaire comme s'il s'agissait d'une seule entité sans avoir à reconstruire les nœuds résultants dans un document. Cela vous permet également de l'utiliser dans une sous-requête/CTE sans modifier le nombre de lignes attendues renvoyées.