ส่งคืนลำดับ 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 ทั้งหมดสำหรับ "แถว" กลับมาคุณสามารถเดินกลับแกน โปรดทราบว่าการเดินกลับสามารถทำให้ประสิทธิภาพของ woogy เล็กน้อยสำหรับบล็อก 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()ฟังก์ชั่นและการใช้containsฟังก์ชั่น XQuery สิ่งที่คุณทำผิดคือ:

  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 โดยไม่ต้องเปลี่ยนจำนวนแถวที่คาดว่าจะถูกส่งกลับ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.