Поверніть послідовності 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Пункт в наведеному вище запиті ліквідує весь набір 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метод вузлів, таким чином:

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

Переміщення квадратних дужок, щоб обернутись навколо вузла електронної пошти, ефективно приводить до того, що WHEREдодаток застосовано до customerвузла. Переклад XQuery на англійську виглядає так:

Знайдіть мені всі xml/customerвузли з emailвузлом, який має addressатрибут, який не містить @символу


4

Ви були так близько. Ви точно були на правильному шляху, використовуючи .query()функцію та використовуючи функцію containsXQuery. Що ви помилилися:

  1. Поставлення = False зовнішньої частини [...](тобто це не було частиною contains()виразу)
  2. Використання слова Falseзамість функціїfalse()
  3. Не вказуючи батьківський вузол, додавши /..до кінця шляху (так що результат буде включати <customer>елемент, а не лише <email>елемент)

Виправлення цих трьох речей призводить до наступного виразу 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;

Цей запит повертає такий набір результатів одного рядка з двома полями 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 за один кадр і не потрібно запускати та зупиняти парсер для кожного вузла.

Інша перевага його збереження .query()полягає в тому, що ви отримуєте один повернений XML-документ. Отже, якщо ви отримуєте документ / значення XML, що містить декілька вузлів, варті речі, ви можете підтримувати підхід до скалярного значення, коли він є єдиною сутністю, без необхідності знову реконструювати отримані вузли в документ. Це також дозволяє використовувати його в підзапиті / CTE, не змінюючи кількість очікуваних рядків, що повертаються.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.