MEMPERBARUI:
Saya telah memperbarui kode, serta input dan output XML dalam contoh pertanyaan di bawah ini untuk mencerminkan persyaratan terbaru, dinyatakan dalam komentar pada jawaban baik @ Mikael , yaitu:
untuk tidak membuat elemen Nilai jika @Nilai kosong atau tidak ada
Sementara satu ekspresi dapat dengan benar mencocokkan variasi baru ini, tampaknya tidak ada cara untuk menghilangkan <Value/>
elemen kosong dalam satu lintasan karena logika kondisional tidak diperbolehkan dalam string pengganti. Jadi, saya telah mengadaptasi ini menjadi modifikasi 2 bagian: satu pass untuk mendapatkan @Value
atribut yang tidak kosong dan satu pass untuk mendapatkan @Value
atribut kosong . Tidak perlu menangani atribut <Element>
yang hilang @Value
karena keinginannya adalah untuk tidak memiliki <Value>
elemen.
Salah satu pilihan adalah memperlakukan XML sebagai string biasa dan mengubahnya berdasarkan suatu pola. Ini mudah dilakukan dengan menggunakan Ekspresi Reguler (khususnya fungsi "Ganti") yang dapat disediakan melalui kode SQLCLR.
Contoh di bawah ini menggunakan skalar UDF RegEx_Replace dari pustaka SQL # (yang saya penulis, tetapi fungsi RegEx ini tersedia dalam versi Gratis, bersama dengan banyak lainnya):
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
The PRINT
pernyataan berada di sana hanya untuk membuat lebih mudah perbandingan sisi-by-side di tab "Pesan". Output yang dihasilkan adalah (saya memodifikasi XML asli sedikit untuk membuatnya sangat jelas bahwa hanya bagian yang diinginkan yang disentuh dan tidak ada yang lain):
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>
Jika Anda ingin memperbarui bidang dalam tabel, Anda bisa menyesuaikan yang di atas sebagai berikut:
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>
elemen per masing-masing<Element>
. Jika tidak, maka memindahkan atribut ke elemen hanya membuat XML lebih kembung, dan mungkin kurang efisien.