2011-09-21 157 views
5

我已經「繼承」輝煌片的TSQL代碼,這是否:複雜TSQL合併

  • 循環行接一行上的光標。
  • 光標包含表A中
  • 需要被合併(Upserted)數據對於在光標存儲的過程被稱爲每行循環。體proc:
    • 如果表A中存在一個對應的行然後被更新
    • 如果這樣的行不存在,那麼:
      • 插入一個單一的行中的一個不同的表B.
      • 抓取操作新生成的ID(稱爲它的IDB)
      • 在表A中插入一行。表A插入需要 IDB(該字段不爲空,它應該只有來自表B的值,但沒有FK約束到位)

顯然,這很爛(性能&優雅的原因)!

問題 起初,這看起來像一個MERGE用法的標準情況。我試圖做:

MERGE [dbo].[TableA] AS Target 
USING <cursor data set as a select statement> as Src on target.IDA = Src.IDA 
WHEN MATCHED 
    //update 
WHEN NOT MATCHED 
//insert <------ Fails because obviously a new IDB is required 

也試過各種方法,如一個nested select that sends IDB on the OUTPUT但它失敗,因爲IDB是一個PK。

其他種類的合併也未能如:

MERGE Table A with <cursor data set as a select statement> 
... 
MERGE Table A with Table B 
WHEN NOT MATCHED 
//insert on Table A 
WHEN NOT MATCHED 
// Update Table B 

有沒有人有這個想法?從本質上講,我認爲,如果我們概括出的問題是:

Can I insert and return the PK in one statement that can be nested in other statements

預先感謝任何答覆

喬治

+2

您可以合併到tableB中,並使用[技術這裏](http://stackoverflow.com/q/5365629/73226)我想。 –

+0

謝謝,看到了。它其實是個好主意。本質上,這個人將新生成的ID存儲在臨時變量中。與我所尋找的不一樣(在1合併中完成整個過程),但它仍然是一個有趣的方法。感謝Martin。 – George

+0

使用單詞'upserted'+1 ... :) –

回答

3

如果您在TableB上有自動生成的PK,則可以使用與此類似的代碼。否則,只需將INSERT更改爲TableA即可首先從TableB中獲取PK。

DECLARE @OldData CHAR(10) 
SET @OldData = 'Old' 
DECLARE @NewData CHAR(10) 
SET @NewData = 'New' 

CREATE TABLE #TableA 
(
    IDA INT IDENTITY(1,1) PRIMARY KEY, 
    IDB INT NOT NULL, 
    DataA CHAR(10) 
) 

CREATE TABLE #TableB 
(
    IDB INT IDENTITY(1,1) PRIMARY KEY, 
    DataB CHAR(10) 
) 

DECLARE @IDsToUpsert TABLE 
(
    ID INT 
) 

-- Add test values for existing rows 
INSERT INTO #TableB 
OUTPUT INSERTED.IDB, @OldData 
INTO #TableA 
SELECT @OldData UNION ALL 
SELECT @OldData UNION ALL 
SELECT @OldData UNION ALL 
SELECT @OldData 

-- Add test values for the rows to upsert 
INSERT INTO @IDsToUpsert 
SELECT 1 UNION -- exists 
SELECT 3 UNION -- exists 
SELECT 5 UNION -- does not exist 
SELECT 7 UNION -- does not exist 
SELECT 9  -- does not exist 

-- Data Before 
SELECT * From #TableA 
SELECT * From #TableB 

DECLARE rows_to_update CURSOR 
    FOR SELECT ID FROM @IDsToUpsert 

DECLARE @rowToUpdate INT 
DECLARE @existingIDB INT 

OPEN rows_to_update; 

FETCH NEXT FROM rows_to_update 
INTO @rowToUpdate; 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    BEGIN TRANSACTION 

     IF NOT EXISTS 
     (
      SELECT 1 FROM #TableA WITH (UPDLOCK, ROWLOCK, HOLDLOCK) 
      WHERE IDA = @rowToUpdate    
     ) 
     BEGIN 
      -- Insert into B, then insert new val into A 
      INSERT INTO #TableB 
      OUTPUT INSERTED.IDB, INSERTED.DataB 
      INTO #TableA 
      SELECT @NewData 
      -- Change code here if PK on TableB is not autogenerated 
     END 
     ELSE 
     BEGIN 
      -- Update 
      UPDATE #TableA 
      SET DataA = @NewData 
      WHERE IDA = @rowToUpdate 
     END 

    COMMIT TRANSACTION 

    FETCH NEXT FROM rows_to_update 
    INTO @rowToUpdate; 
END 

CLOSE rows_to_update; 
DEALLOCATE rows_to_update; 

SELECT * FROM #TableA 
SELECT * FROM #TableB 

DROP TABLE #TableA 
DROP TABLE #TableB 
+0

我認爲它是一個非常好的答案;這<100點海報感謝你;-) – George

+0

謝謝,糟糕的一天...對不起。 – GalacticJello

1

要回答你一般的問題 - 「我可以插入和返回的PK中一個可以嵌套在其他陳述中的陳述' - 是的,絕對的。但這取決於創建PK的邏輯。在這種情況下,似乎要生成一個PK,您需要插入到不同的表中,然後從那裏獲取新生成的ID。這不是很有效(恕我直言),除非有非常具體的理由這樣做。自動增量,GUID等往往比PK更好。如果你可以簡化/改變背後的邏輯,並且你可以找到一個更簡單的方法來實現它,所以PK'CAN'可以在一個語句/函數中生成,因此可以在其他語句中使用。