2012-04-30 29 views
3

我正在嘗試在C#中創建一個小型控制檯應用程序,以便以最快的方式根據XML文件的內容在SQL Server 2008中的產品(ITEMS)的表上執行插入操作。我已經有一個.XSD文件,其中包含適當的映射到SQL表(這可能不需要使用下面概述的方法)。來自XML的SQL Server表上的Upsert

這裏是我的方法的高層次:

  1. 讀取XML,用它來創建一個表。
  2. 使用從XML文件創建的表,對ITEMS表執行MERGE。 2a。如果該項目存在,請更新它。
    2b。如果該項目不存在,請插入它。
  3. 僅創建插入到XML中的記錄的日誌。

考慮以下事項表和XML文件:

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> 

導入後,項目表應該如:

ITEMS

Item_Id Name   Price 
    1  Coke   5.00 
    2  Pepsi   3.00 
    3  Sprite Zero 1.75 
    5  Mountain Dew 4.50 

一旦這樣做了,我也需要生成包含已插入到表(ITEMS_LOG.XML)「新」記錄一個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> 

我曾嘗試使用SQLXMLBulkLoad實現這一點,但遺憾的是它不提供我需要的日誌記錄,也不允許我訪問從SQL Server返回的任何消息(即,什麼被插入/更新)。儘管我擁有SQL專業知識的中級水平,但對於使用XML來說,我相當新,特別是在這種情況下。任何幫助/指導將不勝感激!

+2

工作示例你在哪裏卡住了?你可以發送一些你的代碼嗎? – Zaki

+0

你確定在2個不同的'items.xml'文件中,如果一個產品在數據庫中已經是重複的(通過名稱/價格)1,它將具有相同的ID? – ChrisW

+0

@ChrisW不,他們不一定會有相同的ID。有可能擁有兩個不同ID的相同物品。 – TelJanini

回答

4

您可以使用mergeoutput到表變量,然後查詢表變量建立日誌XML。

將它放入一個存儲過程,其中項目XML作爲in參數,日誌XML作爲out參數。

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 
); 

SE-Data

+0

謝謝@Mikael Eriksson,這對我非常有用。 – GR7

+0

@ GR7你可能也會發現這個有點幫助。 http://stackoverflow.com/questions/16773242/sql-process-xml-performance-insert-into-columns-in-a-table/16773592#16773592 –

+1

再次感謝@Mikael!雖然這對我有用,但當我嘗試使用應用程序中會收到的大量XML數據時,耗時7分鐘,這對我來說並不是真的可以接受。我正在研究SqlBulkCopy將XML放入臨時表並從那裏應用MERGE。有什麼建議? – GR7

0

我會使用臨時表將xml導入到SQL Server表中。添加一個額外的列來指示操作(插入或更新)。然後像平常一樣使用普通的sql來執行upserts。然後,您可以使用登臺表生成所需的XML記錄(讀取操作列以確定它是插入還是更新)。

2

希望這可以幫助你,我做的是創建一個存儲過程如下。基本上,存儲過程採用XML值,並檢查其在代碼傳遞的標誌並確定它是否是插入或更新:

DECLARE @xml xml 
SET @xml = @xmlCredentials 

SELECT 
     item.value('@Id', 'int') As ID, 
     item.value('@AgentID', 'int') As AgentID, 
     item.value('@Username', 'varchar (50)') As Username, 
     item.value('@Password', 'varchar (50)') As [Password], 
     item.value('@IsDirty', 'bit') As IsDirty, 
     item.value('@IsDeleted', 'bit') As IsDeleted 
INTO #tmp 
FROM @xml.nodes('Credentials/Credential') x(item) 

BEGIN TRY 
BEGIN TRAN 
     INSERT INTO Credentials (AgentID, Username, [Password]) 
      SELECT 
       AgentID, Username, [Password] 
      FROM 
       #tmp 
      WHERE 
       ID = 0 AND IsDirty = 1 
     UPDATE c 
     SET c.[AgentID] = t.AgentID, 
      c.[Username] = t.Username, 
      c.[Password] = t.[Password] 
     FROM 
      [dbo].[Credentials] c 
     JOIN 
      #tmp t ON t.Id = c.ID 
     WHERE 
      t.IsDirty = 1 AND t.IsDeleted = 0 

     DELETE FROM [dbo].[Credentials] 
     FROM [dbo].[Credentials] c 
     JOIN #tmp t ON t.Id = c.ID 
     WHERE 
      t.IsDirty = 1 AND t.IsDeleted = 1 

     COMMIT TRAN 
END TRY 
BEGIN CATCH 

     IF @@TRANCOUNT > 0 
      ROLLBACK TRAN 

     DECLARE @errorMSG varchar(4000) 
     DECLARE @errorSeverity int 
     DECLARE @errorState int 

     SET @errorMSG = ERROR_MESSAGE() 
     SET @errorSeverity = ERROR_SEVERITY() 
     SET @errorState = ERROR_STATE() 

     RAISERROR (@errorMSG, 
         @errorSeverity, @errorState); 

END CATCH 

SELECT [ID], [AgentID], [Username], [Password] 
FROM [dbo].[Credentials] 

在後面的代碼我有xml和傳遞XML作爲參數來存儲過程:

// read xml and assign it to string variable 
string xml = readxml(); 

try 
{ 
    string command = "EXEC SaveCredentails '" + xml + "'"; 
} 
catch(Exception e) 
{ 
}