Trả về các chuỗi xml trong đó một thuộc tính không chứa một ký tự cụ thể


10

Hãy xem xét XML đơn giản sau:

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

Tôi muốn có được một danh sách các <Customer>chuỗi trong đó addressthuộc tính của <email>vật phẩm không chứa @.

Vì vậy, tôi muốn đầu ra trông như:

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

Truy vấn này:

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

Trả về:

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

Truy vấn này:

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

Trả về:

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

Các WHEREđiều khoản trong các truy vấn trên được loại bỏ toàn bộ các XML vì ít nhất một chuỗi duy nhất tồn tại nơi địa chỉ email có chứa một hiệu "@".

Câu trả lời:


11

Một cách dễ dàng để làm điều này là sử dụng nodes phương thức để vào đúng addressthuộc tính và kiểm tra @dấu hiệu của bạn .

Vấn đề với cách bạn nhìn bây giờ là nó chỉ kiểm tra xem có địa chỉ email nào@trong đó không. Phân tích các nút XML ra cho phép bạn kiểm tra các email riêng lẻ cho nó.

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;

Nếu bạn cần truy vấn một bảng thực tế với một cột XML như thế này, thì bạn chỉ cần CROSS APPLYphương thức các nút như vậy:

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;

Nếu bạn muốn mang tất cả <customer>...</customer>XML cho "hàng" đó trở lại, bạn có thể đưa trục quay trở lại. Chỉ cần lưu ý rằng việc quay trở lại có thể làm cho hiệu suất trở nên hơi gượng gạo đối với các khối XML lớn.

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;

Một cách khác để làm điều đó là:

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

Di chuyển dấu ngoặc vuông để bao quanh nút email một cách hiệu quả làm cho WHEREmệnh đề được áp dụng cho customernút. Dịch XQuery này sang tiếng Anh trông giống như:

Nhận cho tôi tất cả xml/customercác nút có một emailnút có addressthuộc tính không chứa @ký hiệu


4

Bạn đã rất gần. Bạn chắc chắn đã đi đúng hướng khi sử dụng .query()hàm và sử dụng containshàm XQuery. Những gì bạn đã sai là:

  1. Đặt = False bên ngoài của [...](có nghĩa, nó không phải là một phần của contains()biểu thức)
  2. Sử dụng từ Falsethay cho chức năngfalse()
  3. Không chỉ định nút cha bằng cách thêm /..vào cuối đường dẫn (để kết quả sẽ bao gồm <customer>phần tử chứ không chỉ <email>phần tử)

Sửa ba điều đó dẫn đến biểu thức XQuery sau đây mang lại cho bạn những gì bạn muốn:

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

Đặt điều đó vào ví dụ ban đầu của bạn từ câu hỏi mang lại cho bạn:

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;

Truy vấn đó trả về tập kết quả sau của một hàng với hai trường 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>                               |

Điều này có lẽ hiệu quả hơn việc phá vỡ tài liệu bằng .nodes()hàm vì nó có thể phân tích cú pháp XML trong một lần chụp và không cần phải bắt đầu và dừng trình phân tích cú pháp trên mỗi nút.

Lợi ích khác của việc giữ nó bên trong .query()là bạn nhận được một tài liệu XML duy nhất được trả về. Vì vậy, nếu bạn nhận được một tài liệu / giá trị XML chứa nhiều giá trị của các nút, bạn có thể duy trì cách tiếp cận giá trị vô hướng của nó là một thực thể duy nhất mà không phải xây dựng lại các nút kết quả trở lại thành tài liệu. Điều này cũng cho phép bạn sử dụng nó trong truy vấn con / CTE mà không thay đổi số lượng hàng dự kiến ​​được trả về.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.