۱۳۸۸/۰۶/۱۳

به روز رساني فيلدهاي XML در SQL Server


از SQL server 2005 به بعد، پشتيباني كاملي از XML توسط اين محصول صورت مي‌گيرد. در ادامه مروري خواهيم داشت بر نحوه‌ي به روز رساني مقادير فيلدهايي از نوع XML در SQL Server .
در ابتدا جدول موقتي زير را كه شامل يك ركورد از نوع XML است، در نظر بگيريد:

DECLARE @tblTest AS TABLE (xmlField XML)

INSERT INTO @tblTest
(
xmlField
)
VALUES
(
'<Sample>
<Node1>Value1</Node1>
<Node2>Value2</Node2>
<Node3>OldValue</Node3>
</Sample>'
)
مي‌خواهيم OldValue را به مقداري ديگر تغيير دهيم.

سعي اول:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'
UPDATE @tblTest
SET xmlField.modify('replace value of (/Sample/Node3)[1] with ' + @newValue)
اين سعي با خطاي زير متوقف مي‌شود:

The argument 1 of the XML data type method "modify" must be a string literal.
بنابراين از روش String concatenation براي معرفي مقدار متغير مورد نظر در اينجا نمي‌شود استفاده كرد.

سعي دوم:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3)[1] with sql:variable("@newValue")'
)
روش معرفي صحيح يك متغير را در اينجا مي‌توان مشاهده كرد. اما اين سعي نيز با خطاي زير متوقف مي‌شود:

XQuery [@tblTest.xmlField.modify()]: The target of 'replace value of' must be a non-metadata attribute or an element with simple typed content, found 'element(NodeThree,xdt:untyped) ?'

سعي سوم:

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

و بله. كار مي‌كنه!
XML ايي را كه در ابتدا استفاده كرديم از نوع un-typed XML محسوب شده و هيچ schema ايي را براي آن در نظر نگرفته‌ايم، به همين جهت بايد دقيقا مشخص كنيم كه قصد داريم text اين node را ويرايش نمائيم.

مشكل بعدي!
در ابتدا مثال زير را در نظر بگيريد:

DECLARE @tblTest AS TABLE (xmlField XML)

INSERT INTO @tblTest
(
xmlField
)
VALUES
(
'<Sample>
<Node1>Value1</Node1>
<Node2>Value2</Node2>
<Node3></Node3>
</Sample>'
)

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest

اين عبارات T-SQL ، خلاصه بحث ما تا به اينجا هستند اما با يك تفاوت. نود 3 در اينجا خالي است.
اگر اسكريپت را اجرا كنيد، هيچ تغييري را مشاهده نخواهيد كرد. به عبارت ديگر به روز رساني صورت نمي‌گيرد. در اينجا چون text اين نود خالي است ، فرض SQL Server بر اين خواهد بود كه وجود ندارد، بنابراين اين نود را به روز رساني نخواهد كرد. به همين منظور بايد براي به روز رساني اين نود، عبارت جديد را در جايي كه text ندارد insert‌ كرد (و نه replace).

DECLARE @newValue VARCHAR(50)
SELECT @newValue = 'NewValue'

UPDATE @tblTest
SET xmlField.modify(
'replace value of (/Sample/Node3/text())[1]
with sql:variable("@newValue")'
)

UPDATE @tblTest
SET xmlField.modify(
'insert text{sql:variable("@newValue")} into
(/Sample/Node3)[1] [not(text())]'
)

SELECT xmlField.value('(/Sample/Node3)[1]','varchar(50)') FROM @tblTest