2013-06-13 22 views
0

我有一個分佈式事務,我需要合併到目標遠程表。 根據MSDN,現在不允許MERGE INTO:「target_table不能是遠程表」。MSSQL合併分佈式事務替代

所以我的解決方法如下:0.開始分佈式事務1.定義一個遊標2.打開它3.如果遊標至少有一個記錄(CURSOR_STATUS()= 1)取下一個4. if exists(select top 1 * from target_remote_table其中id = @myCurrentCursorId) - > true時更新target_remote_table當false插入到target_remote_table時5.提交/回滾分佈式事務,取決於trancount和xact_state

它的作品,但我知道遊標是邪惡的,不要使用它們。所以我想問問是否有其他方法可以通過不使用遊標來解決這個問題?

USE [My_DB] 
GO 

SET ANSI_NULLS ON 
GO 
SET QUOTED_IDENTIFIER ON 
GO 
CREATE PROCEDURE [dbo].[my_proc_merge_into_remote_table] 
@ID_A INT, 
@ID_B INT 

AS 
BEGIN 
SET NOCOUNT ON; 

-- CURSOR VALUES 
DECLARE @field_A INT 
DECLARE @field_B INT 
DECLARE @field_C INT 
DECLARE @field_D BIT 
DECLARE @field_E INT 
DECLARE @field_F DATETIME 
DECLARE @field_G VARCHAR(20) 
DECLARE @field_H DATETIME 
DECLARE @field_I VARCHAR(20) 


BEGIN TRY 

    BEGIN DISTRIBUTED TRANSACTION 

    -- CURSOR !! 
    DECLARE my_cursor CURSOR FOR 
    SELECT  b.field_A , 
       b.field_B, 
       c.field_C, 
       a.field_D, 
       a.field_E, 
       GETDATE() AS field_F, 
       a.field_G, 
       GETDATE() AS field_H, 
       a.field_I 
     FROM dbo.source_tbl a 
     LEFT JOIN dbo.base_element_tbl l 
      ON a.obj_id = l.obj_id AND a.element_id = l.element_id 
     INNER JOIN dbo.base_obj_tbl b 
      ON a.obj_id = b.obj_id 
     INNER JOIN dbo.element_tbl c 
      ON a.element_id = c.element_id 
     WHERE a.ID_B = @ID_B 
      AND a.ID_A = @ID_A; 


    OPEN my_cursor; 


    -- check if cursor result set has at least one row 
    IF CURSOR_STATUS('global', 'my_cursor') = 1 BEGIN 

     FETCH NEXT FROM my_cursor 
      INTO @field_A, 
       @field_B, 
       @field_C, 
       @field_D, 
       @field_E, 
       @field_F, 
       @field_G, 
       @field_H, 
       @field_I; 


     WHILE @@FETCH_STATUS = 0 BEGIN 
      -- HINT: MY_REMOTE_TARGET_TABLE is a Synonym which already points to the correct database and table 
      IF EXISTS(SELECT TOP 1 * FROM MY_REMOTE_TARGET_TABLE WHERE field_A = @field_A AND field_B = @field_B AND field_C = @field_C AND field_E = @field_E) 
       UPDATE MY_REMOTE_TARGET_TABLE SET field_D = @field_D, field_H = @field_H, field_I = @field_I; 
      ELSE 
       INSERT INTO MY_REMOTE_TARGET_TABLE (field_A, field_B, field_C, field_D, field_E, field_F, field_G, field_H, field_I) VALUES (@field_A, @field_B, @field_C, @field_D, @field_E, @field_F, @field_G, @field_H, @field_I); 

      FETCH NEXT FROM my_cursor 
       INTO @field_A, 
        @field_B, 
        @field_C, 
        @field_D, 
        @field_E, 
        @field_F, 
        @field_G, 
        @field_H, 
        @field_I; 

     END; 
    END; 

    CLOSE my_cursor; 
    DEALLOCATE my_cursor; 


    IF (@@TRANCOUNT > 0 AND XACT_STATE() = 1) 
    BEGIN 
     COMMIT TRANSACTION 
    END  


END TRY 
BEGIN CATCH 
    IF (@@TRANCOUNT > 0 AND XACT_STATE() = -1)   
    ROLLBACK TRANSACTION 

END CATCH; 

END 
+0

MERGE的目標不能是遠程表,但Source可以。您可以從遠程服務器運行您的查詢。 –

+0

另外,任何可以用MERGE寫的東西也可以用INSERT/UPDATE/DELETE組合 –

+0

來重寫,我會嘗試從遠程服務器中開始合併。 「也可以用INSERT/UPDATE/DELETE重寫」,這正是我所做的,它只是您需要決定是否需要插入或更新的部分,這很棘手。截至目前,我通過光標選擇每條記錄來做到這一點,並一個接一個地檢查它是否已經存在。我只是想知道是否有比主機服務器更好的解決方案。光標解決方案也很慢,所以這是另一個原因。 – BaseBallBatBoy

回答

0

在這裏,你先去內部連接目標和源更新,然後左連接插入缺失。

;WITH CTE_Source AS 
( 
    SELECT b.field_A , 
      b.field_B, 
      c.field_C, 
      a.field_D, 
      a.field_E, 
      GETDATE() AS field_F, 
      a.field_G, 
      GETDATE() AS field_H, 
      a.field_I 
     FROM dbo.source_tbl a 
     LEFT JOIN dbo.base_element_tbl l 
      ON a.obj_id = l.obj_id AND a.element_id = l.element_id 
     INNER JOIN dbo.base_obj_tbl b 
      ON a.obj_id = b.obj_id 
     INNER JOIN dbo.element_tbl c 
      ON a.element_id = c.element_id 
     WHERE a.ID_B = @ID_B 
      AND a.ID_A = @ID_A; 
) 
UPDATE trgt 
SET trgt.field_D = src.field_D, trgt.field_H = src.field_H, trgt.field_I = src.field_I 
FROM MY_REMOTE_TARGET_TABLE trgt 
INNER JOIN CTE_Source src ON src.field_A = trgt.field_A AND src.field_B = trgt.field_B AND src.field_C = trgt.field_C AND src.field_E = trgt.field_E 


;WITH CTE_Source AS 
( 
    SELECT b.field_A , 
      b.field_B, 
      c.field_C, 
      a.field_D, 
      a.field_E, 
      GETDATE() AS field_F, 
      a.field_G, 
      GETDATE() AS field_H, 
      a.field_I 
     FROM dbo.source_tbl a 
     LEFT JOIN dbo.base_element_tbl l 
      ON a.obj_id = l.obj_id AND a.element_id = l.element_id 
     INNER JOIN dbo.base_obj_tbl b 
      ON a.obj_id = b.obj_id 
     INNER JOIN dbo.element_tbl c 
      ON a.element_id = c.element_id 
     WHERE a.ID_B = @ID_B 
      AND a.ID_A = @ID_A; 
) 
INSERT INTO MY_REMOTE_TARGET_TABLE (field_A, field_B, field_C, field_D, field_E, field_F, field_G, field_H, field_I) 
SELECT field_A, field_B, field_C, field_D, field_E, field_F, field_G, field_H, field_I 
FROM CTE_Soure src 
LEFT JOIN MY_REMOTE_TARGET_TABLE trgt ON src.field_A = trgt.field_A AND src.field_B = trgt.field_B AND src.field_C = trgt.field_C AND src.field_E = trgt.field_E 
WHERE trgt.field_A IS NULL 
+0

非常感謝!我的思想真的被困在這個上面。乾杯 – BaseBallBatBoy