UPDATE:
ฉันได้อัปเดตโค้ดรวมถึง XML อินพุตและเอาต์พุตในเคียวรีตัวอย่างด้านล่างเพื่อแสดงถึงข้อกำหนดล่าสุดที่ระบุไว้ในความคิดเห็นในคำตอบที่ดีของ @ Mikael ซึ่งก็คือ:
เพื่อไม่สร้างองค์ประกอบค่าหาก @Value ว่างเปล่าหรือไม่มีอยู่
ในขณะที่นิพจน์เดียวสามารถจับคู่รูปแบบใหม่นี้ได้อย่างถูกต้องดูเหมือนจะไม่มีทางที่จะละเว้น<Value/>
องค์ประกอบที่ว่างเปล่าในการส่งครั้งเดียวเนื่องจากตรรกะเงื่อนไขที่ไม่ได้รับอนุญาตในสตริงการเปลี่ยน ดังนั้นฉันได้ดัดแปลงสิ่งนี้ให้เป็นการดัดแปลง 2 ส่วน: หนึ่งรอบเพื่อรับ@Value
แอตทริบิวต์ที่ไม่ว่างเปล่าและหนึ่งรอบเพื่อให้ได้@Value
แอตทริบิวต์ที่ว่างเปล่า ไม่จำเป็นต้องจัดการ<Element>
กับ@Value
คุณลักษณะที่ขาดหายไปเนื่องจากความปรารถนาที่จะไม่มี<Value>
องค์ประกอบอยู่ดี
ทางเลือกหนึ่งคือการปฏิบัติ XML เป็นสตริงปกติและแปลงมันตามรูปแบบ สิ่งนี้สามารถทำได้อย่างง่ายดายโดยใช้นิพจน์ปกติ (โดยเฉพาะฟังก์ชั่น "แทนที่") ซึ่งสามารถทำได้ผ่านรหัส SQLCLR
ตัวอย่างด้านล่างใช้UDF สเกลาร์RegEx_ReplaceจากไลบรารีSQL # (ซึ่งฉันเป็นผู้เขียน แต่ฟังก์ชัน RegEx นี้มีให้บริการในรุ่นฟรีพร้อมด้วยอื่น ๆ อีกมากมาย):
DECLARE @SomeXml XML;
SET @SomeXml = N'<Root attr1="val1" attr2="val2">
<Elements>
<Element Code="1" Value="aaa" ExtraData="extra1" />
<Element Code="22" Value="bbb" ExtraData="extra2" />
<Element Code="333" Value="ccc" ExtraData="extra3" />
<Element Code="4444" Value="" ExtraData="extra4" />
<Element Code="55555" ExtraData="extra5" />
</Elements>
<ExtraData>
<Something Val="1">qwerty A</Something>
<Something Val="2">qwerty B</Something>
</ExtraData>
</Root>';
DECLARE @TempStringOfXml NVARCHAR(MAX),
@Expression NVARCHAR(4000),
@Replacement NVARCHAR(4000);
SET @TempStringOfXml = CONVERT(NVARCHAR(MAX), @SomeXml);
PRINT N'Original: ' + @TempStringOfXml;
---
SET @Expression =
N'(<Element Code="[^"]+")\s+Value="([^"]+)"\s+(ExtraData="[^"]+")\s*/>';
SET @Replacement = N'$1 $3><Value>$2</Value></Element>';
SELECT @TempStringOfXml = SQL#.RegEx_Replace(@TempStringOfXml, @Expression,
@Replacement, -1, 1, '');
PRINT '-------------------------------------';
PRINT N'Phase 1: ' + @TempStringOfXml; -- transform Elements with a non-empty @Value
---
SET @Expression = N'(<Element Code="[^"]+")\s+Value=""\s+(ExtraData="[^"]+")\s*/>';
SET @Replacement = N'$1 $2 />';
SELECT @TempStringOfXml = SQL#.RegEx_Replace(@TempStringOfXml, @Expression,
@Replacement, -1, 1, '');
PRINT '-------------------------------------';
PRINT N'Phase 2: ' + @TempStringOfXml; -- transform Elements with an empty @Value
SELECT CONVERT(XML, @TempStringOfXml); -- prove that this is valid XML
PRINT
งบมีอยู่ในเพียงเพื่อให้สำหรับการเปรียบเทียบด้านข้างได้ง่ายขึ้นในแท็บ "ข้อความ" ผลลัพธ์ที่ได้คือ (ฉันปรับเปลี่ยน XML ดั้งเดิมเล็กน้อยเพื่อให้ชัดเจนมากว่าเฉพาะส่วนที่ต้องการเท่านั้นที่สัมผัสและไม่มีอะไรอื่น):
Original: <Root attr1="val1" attr2="val2"><Elements><Element Code="1" Value="aaa" ExtraData="extra1"/><Element Code="22" Value="bbb" ExtraData="extra2"/><Element Code="333" Value="ccc" ExtraData="extra3"/><Element Code="4444" Value="" ExtraData="extra4"/><Element Code="55555" ExtraData="extra5"/></Elements><ExtraData><Something Val="1">qwerty A</Something><Something Val="2">qwerty B</Something></ExtraData></Root>
-------------------------------------
Phase 1: <Root attr1="val1" attr2="val2"><Elements><Element Code="1" ExtraData="extra1"><Value>aaa</Value></Element><Element Code="22" ExtraData="extra2"><Value>bbb</Value></Element><Element Code="333" ExtraData="extra3"><Value>ccc</Value></Element><Element Code="4444" Value="" ExtraData="extra4"/><Element Code="55555" ExtraData="extra5"/></Elements><ExtraData><Something Val="1">qwerty A</Something><Something Val="2">qwerty B</Something></ExtraData></Root>
-------------------------------------
Phase 2: <Root attr1="val1" attr2="val2"><Elements><Element Code="1" ExtraData="extra1"><Value>aaa</Value></Element><Element Code="22" ExtraData="extra2"><Value>bbb</Value></Element><Element Code="333" ExtraData="extra3"><Value>ccc</Value></Element><Element Code="4444" ExtraData="extra4" /><Element Code="55555" ExtraData="extra5"/></Elements><ExtraData><Something Val="1">qwerty A</Something><Something Val="2">qwerty B</Something></ExtraData></Root>
หากคุณต้องการอัปเดตเขตข้อมูลในตารางคุณสามารถปรับตัวด้านบนให้เป็นดังต่อไปนี้:
DECLARE @NonEmptyValueExpression NVARCHAR(4000),
@NonEmptyValueReplacement NVARCHAR(4000),
@EmptyValueExpression NVARCHAR(4000),
@EmptyValueReplacement NVARCHAR(4000);
SET @NonEmptyValueExpression =
N'(<Element Code="[^"]+")\s+Value="([^"]+)"\s+(ExtraData="[^"]+")\s*/>';
SET @NonEmptyValueReplacement = N'$1 $3><Value>$2</Value></Element>';
SET @EmptyValueExpression =
N'(<Element Code="[^"]+")\s+Value=""\s+(ExtraData="[^"]+")\s*/>';
SET @EmptyValueReplacement = N'$1 $2 />';
UPDATE tbl
SET XmlField = SQL#.RegEx_Replace4k(
SQL#.RegEx_Replace4k(
CONVERT(NVARCHAR(4000), tbl.XmlField),
@NonEmptyValueExpression,
@NonEmptyValueReplacement,
-1, 1, ''),
@EmptyValueExpression,
@EmptyValueReplacement,
-1, 1, '')
FROM SchemaName.TableName tbl
WHERE tbl.XmlField.exist('Root/Elements/Element/@Value') = 1;
<Value>
<Element>
ถ้าไม่เช่นนั้นการย้ายแอตทริบิวต์ไปยังองค์ประกอบจะทำให้ XML พองตัวและมีประสิทธิภาพน้อยกว่า