Restituisce sequenze xml in cui un attributo non contiene un carattere specifico


10

Considera il seguente XML semplice:

<xml>
  <customer name="Max">
    <email address="me@you.com" />
  </customer>
  <customer name="Erik">
    <email address="erik@your-mom.com" />
  </customer>
  <customer name="Brent">
    <email address="brentcom" />
  </customer>
</xml>

Voglio ottenere un elenco di <Customer>sequenze in cui l' addressattributo <email>dell'articolo non contiene un @.

Quindi, voglio un output simile a:

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

mcve :

DECLARE @x XML = '<xml>
<customer name="Max"><email address="me@you.com" /></customer>
<customer name="Erik"><email address="erik@your-mom.com" /></customer>
<customer name="Brent"><email address="brentcom" /></customer>
</xml>';

Questa query:

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

Ritorna:

╔═══════════════════════════════════════╦══════════════════╗
            WithValidEmail              WithInvalidEmail 
╠═══════════════════════════════════════╬══════════════════╣
 <email address="me@you.com" />                          
 <email address="erik@your-mom.com" />  false            
╚═══════════════════════════════════════╩══════════════════╝

Questa query:

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

Ritorna:

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

La WHEREclausola nella query sopra sta eliminando l'intero set di XML perché esiste almeno una singola sequenza in cui l'indirizzo e-mail contiene un segno "@".

Risposte:


11

Un modo semplice per farlo è utilizzare il nodes metodo per ottenere l' addressattributo e verificare il @segno.

Il problema con il modo in cui stai guardando ora è che sta solo controllando che qualsiasi indirizzo e-mail abbia un indirizzo @. L'analisi dei nodi XML consente di controllare le singole e-mail.

DECLARE @x XML
    = '<xml>
<customer name="Max"><email address="me@you.com" /></customer>
<customer name="Erik"><email address="erik@your-mom.com" /></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;

Se hai bisogno di interrogare una tabella reale con una colonna XML come questa, dovresti solo CROSS APPLYil metodo dei nodi in questo modo:

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;

Se vuoi riportare tutto l' <customer>...</customer>XML per quella "riga", puoi tornare indietro sull'asse. Basta essere consapevoli del fatto che tornare indietro può rendere le prestazioni un po 'traballanti per blocchi XML di grandi dimensioni.

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;

Un altro modo di farlo è:

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

Spostando le parentesi quadre per avvolgere il nodo e-mail, la WHEREclausola si applica al nodo in modo efficace customer. Tradurre questo XQuery in inglese è simile a:

Ottieni tutti i xml/customernodi con un emailnodo che ha un addressattributo che non contiene il @simbolo


4

Eri così vicino. Sei stato sicuramente sulla strada giusta usando la .query()funzione e usando la containsfunzione XQuery. Quello che hai sbagliato è stato:

  1. Mettere l' = False esterno del [...](significato, non faceva parte contains()dell'espressione)
  2. Usando la parola Falseinvece della funzionefalse()
  3. Non specificare il nodo padre aggiungendo /..alla fine del percorso (in modo che il risultato includa l' <customer>elemento e non solo l' <email>elemento)

La correzione di queste tre cose si traduce nella seguente espressione XQuery che ti dà ciò che desideri:

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

Metterlo nel tuo esempio originale dalla domanda ti dà:

DECLARE @x XML = '<xml>
<customer name="Max"><email address="me@you.com" /></customer>
<customer name="Erik"><email address="erik@your-mom.com" /></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;

Quella query restituisce il seguente set di risultati di una singola riga con due campi XML:

WithValidEmail                            |     WithInvalidEmail
<customer name="Max">                     |     <customer name="Brent">
  <email address="me@you.com" />          |       <email address="brentcom" />
</customer>                               |     </customer>
<customer name="Erik">                    |
  <email address="erik@your-mom.com" />   |
</customer>                               |

Questo è probabilmente più efficiente rispetto alla suddivisione del documento con la .nodes()funzione poiché può analizzare l'XML in una singola ripresa e non è necessario avviare e arrestare il parser per ciascun nodo.

L'altro vantaggio di mantenerlo all'interno .query()è la restituzione di un singolo documento XML. Pertanto, se si riceve un documento / valore XML contenente più nodi di valore, è possibile mantenere l'approccio del valore scalare in quanto singola entità senza dover ricostruire nuovamente i nodi risultanti in un documento. Ciò consente inoltre di utilizzarlo in una sottoquery / CTE senza modificare il numero di righe previste restituite.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.