Jay
Jay

Reputation: 2200

Update XML with value from another (non-xml) column in T-SQL

I have a table with two fields of type NUMERIC and one field of type XML. Here is a rough sample:

CREATE TABLE books (
        ID INT NOT NULL,
        price NUMERIC(4,2),
        discount NUMERIC(2,2),
        book XML
);

The XML value will look something like, say,

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <title>Harry Potter</title>
    <author>J K Rowling</author>
    <Store>
        <Name>Burke and Burkins</Name>
        <Address>Some St, Somewhere, Some City</Address>
    </Store>
</book>

Now my question is, using xml.modify(), how can I add two xpaths under Store with the price and discount with value from books.price and books.discount?

<?xml version="1.0" encoding="UTF-8"?>
<book>
    <title>Harry Potter</title>
    <author>J K Rowling</author>
    <Store>
        <Name>Burke and Burkins</Name>
        <Address>Some St, Somewhere, Some City</Address>
        <Price>value from books.price from the same row</Price>
        <Discount>value from books.discount from the same row</Discount>
    </Store>
</book>

This is a rough example, so please don't worry about where the XML data came from. Lets just say the book column has the XML data already present.

I know how to update the table with static values with,

UPDATE books
SET book.modify('insert <Price>10.99</Price><Discount>20.00</Discount> after (/book/Store/Address)[1]')

Performance is not a consideration here.

Upvotes: 4

Views: 501

Answers (1)

Gottfried Lesigang
Gottfried Lesigang

Reputation: 67291

It is not possible to do two modifications in one statement.

In this case you might trick this out by first combining both values and then insert them at once.

I use an updateable CTE to achieve this:

CREATE TABLE books (
        ID INT NOT NULL,
        price NUMERIC(4,2),
        discount NUMERIC(2,2),
        book XML
);

--Fill the table with data

INSERT INTO books VALUES(1,10.5,.5,
'<book>
    <title>Harry Potter</title>
    <author>J K Rowling</author>
    <Store>
        <Name>Burke and Burkins</Name>
        <Address>Some St, Somewhere, Some City</Address>
    </Store>
</book>');

--This is the actual query

WITH CTE AS
(
    SELECT *
          ,(SELECT price AS Price,discount AS Discount FOR XML PATH(''),TYPE) AS XmlNode 
    FROM books
)
UPDATE CTE SET book.modify('insert sql:column("XmlNode") after (/book/Store/Address)[1]');

--Check the result

SELECT *
FROM books;

--Clean-Up (carefull with real date!)

GO
--DROP TABLE books;

One hint

Your XML column, if it is really XML, will - for sure! - not contain an XML starting with <?xml version="1.0" encoding="UTF-8"?>. The internal encoding is always unicode (ucs-2, which is almost utf-16) and one cannot change this. If you pass in a declaration, it is either ommited or you'll get an error.

UPDATE

Another approach was to first read the XML's values and then to rebuild it:

WITH CTE AS
(
    SELECT *
          ,(SELECT b.value('title[1]','nvarchar(max)') AS [title]
                  ,b.value('author[1]','nvarchar(max)') AS [author]
                  ,b.value('(Store/Name)[1]','nvarchar(max)') AS [Store/Name]
                  ,b.value('(Store/Address)[1]','nvarchar(max)') AS [Store/Address]
                  ,price AS [Store/Price]
                  ,discount AS [Store/Discount]
            FROM book.nodes('book') AS A(b)
            FOR XML PATH('book'),TYPE
            ) AS bookNew
    FROM books
)
UPDATE CTE SET book=bookNew;

Upvotes: 4

Related Questions