属性に特定の文字が含まれていないXMLシーケンスを返す


10

次の単純な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>

アイテム<Customer>address属性にが含まれて<email>ないシーケンスのリストを取得したい@

したがって、次のような出力が必要です。

<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>';

このクエリ:

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

戻り値:

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

このクエリ:

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

戻り値:

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

WHERE上記のクエリの句では、電子メールアドレスに「@」記号が含まれる場所に少なくとも1つのシーケンスが存在するため、XMLのセット全体が削除されています。

回答:


11

これを行う簡単な方法は、nodes メソッドを使用してaddress属性に直接アクセスし、@サインを確認することです。

あなたが今見ている方法に問題はそれだけでことを確認だということである任意のメールアドレスを持っている@ことに。XMLノードを解析して、個々の電子メールをチェックできます。

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;

このようなXML列を含む実際のテーブルをクエリする必要がある場合はCROSS APPLY、nodesメソッドだけで次のようになります。

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;

<customer>...</customer>その「行」のすべてのXMLを戻したい場合は、軸を元に戻すことができます。大規模な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;

それを行う別の方法は次のとおりです。

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

大括弧を移動してEメール・ノードをラップすると、WHEREその節がノードに効果的に適用されcustomerます。このXQueryを英語に翻訳すると、次のようになります。

シンボルを含まない属性を持つノードを持つすべてのxml/customerノードを取得しますemailaddress@


4

あなたはとても近かった。.query()関数の使用とcontainsXQuery関数の使用は間違いなく正しい方向に進んでいました。あなたが間違ったことは:

  1. = False 外側に置く[...](つまり、contains()表現の一部ではなかった)
  2. False関数の代わりに単語を使用するfalse()
  3. /..パスの最後に追加して親ノードを指定しない(結果には<customer>要素だけでなく要素も含まれるようにするため<email>

これら3つを修正すると、次のXQuery式が得られ、必要なものが得られます。

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

それを質問の元の例に入れると、次のようになります。

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;

このクエリは、2つの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>                               |

これは.nodes()、XMLを単一のショットで解析でき、ノードごとにパーサーを開始および停止する必要がないため、関数でドキュメントを分割するよりもおそらく効率的です。

内部に保持することのもう1つの利点は.query()、単一のXMLドキュメントが返されることです。したがって、複数のノードに相当するものを含むXMLドキュメント/値を受け取った場合、結果のノードをドキュメントに再構築する必要なく、単一のエンティティであるというスカラー値のアプローチを維持できます。これにより、返される期待される行数を変更せずに、サブクエリ/ CTEで使用することもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.