Upsert on SQL Server table from XML

.net, c#, sql-server-2008, t-sql, xml

Solution

You can use `merge` with `output` to a table variable and then query the table variable to build the log XML.

Put it in a stored procedure where you have the item XML as an in parameter and the log XML as an out parameter.

create procedure AddItemXML
  @ItemsXML xml,
  @ItemsLogXML xml out
as

declare @Changes table
(
  Item_Id int,
  Name nvarchar(20),
  Price money,
  Action nvarchar(10)
);

merge Items as T
using
  (
    select T.N.value('Id[1]', 'int') as Item_Id,
           T.N.value('Name[1]', 'varchar(20)') as Name,
           T.N.value('Price[1]', 'money') as Price
    from @ItemsXML.nodes('/Item') T(N)
  ) as S
on T.Item_Id = S.Item_Id
when matched then
  update set Name = S.Name, Price = S.Price
when not matched then
  insert (Item_Id, Name, Price) values (S.Item_Id, S.Name, S.Price)
output inserted.Item_Id,
       inserted.Name,
       inserted.Price,
       $action 
  into @Changes;

set @ItemsLogXML = 
  (
    select Item_Id as ID,
           Name,
           Price
    from @Changes
    where Action = 'INSERT'
    for xml path('Item'), type
  );

Working sample on SE-Data

Problem

I'm attempting to create a small console app in C# to perform inserts on a table of Products (ITEMS) in SQL Server 2008 according to the contents of an XML file in the FASTEST way possible. I already have an .XSD file that contains the proper mappings to the SQL table (which may not be necessary with the approach outlined below). Here's a high-level of my approach: - Read the XML, using it to create a table. - Perform a MERGE against the ITEMS table using the table created from the XML file. 2a. If the item exists, update it. 2b. If the item does not exist, insert it. - Create a log of only the records inserted in XML. Consider the following ITEMS table and XML file: ITEMS ``` Item_Id Name Price 1 Coke 5.00 2 Pepsi 3.00 3 Sprite 2.00 ``` ITEMS.XML ``` <?xml version="1.0" encoding="ISO-8859-1"?> <Item> <Id>5</Id> <Name>Mountain Dew</Name> <Price>4.50</Price> </Item> <Item> <Id>3</Id> <Name>Sprite Zero</Name> <Price>1.75</Price> </Item> ``` After the import, the ITEMS table should look like: ITEMS ``` Item_Id Name Price 1 Coke 5.00 2 Pepsi 3.00 3 Sprite Zero 1.75 5 Mountain Dew 4.50 ``` Once that's done, I also need to generate an XML formatted log file that contains the "new" record that was inserted into the table (ITEMS_LOG.XML): ITEMS_LOG.XML ``` <?xml version="1.0" encoding="ISO-8859-1"?> <Item> <Id>5</Id> <Name>Mountain Dew</Name> <Price>4.50</Price> </Item> ``` I have tried implementing this using SQLXMLBulkLoad, but unfortunately it does not provide the logging that I need, nor does it permit me to access any of the messages returned from SQL Server (i.e. what's been inserted/updated). Although I have an intermediate level of SQL expertise, I am fairly new to working with XML, especially in this context. Any help/guidance would be greatly appreciated!

Original source

Related problems