2011-09-21 15 views
5

मैं है 'विरासत में मिला' TSQL कोड का एक शानदार टुकड़ा है कि इस करता है:परिसर TSQL मर्ज

  • लूप्स पंक्ति-दर-पंक्ति एक कर्सर।
  • कर्सर कर्सर एक संग्रहीत proc कहा जाता है में प्रत्येक पंक्ति के पाश के लिए तालिका A
  • में की जरूरत है, जिन्हें मर्ज करने (Upserted) डेटा होता है। proc:
    • एक इसी पंक्ति तालिका A में मौजूद है, तो यह अद्यतन किया जाता है
    • अगर इस तरह के एक पंक्ति तो मौजूद नहीं है:
      • आवेषण एक अलग तालिका बी में में एक ही पंक्ति
      • को हासिल करेगा नव निर्मित आईडी (जिसे इसे आईडीबी कहा जाता है)
      • तालिका ए तालिका में एक पंक्ति को सम्मिलित करता है एक प्रविष्टि एक आईडीबी की आवश्यकता है (फ़ील्ड शून्य नहीं है, यह केवल तालिका बी से मूल्य होना चाहिए, लेकिन कोई FK नहीं है बाधा जगह में है)

जाहिर है इस बेकार है (प्रदर्शन & लालित्य कारणों) !!

प्रश्न सबसे पहले इस मर्ज उपयोग के एक मानक केस की तरह दिखता है। मैं कर की कोशिश की:

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 जैसे विभिन्न दृष्टिकोण की कोशिश की लेकिन यह विफल रहता है क्योंकि आईडीबी एक पी है।

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

आप टेबलबी में विलय कर सकते हैं और [तकनीक यहाँ] (http://stackoverflow.com/q/5365629/73226) का उपयोग कर सकते हैं। –

+0

धन्यवाद, उसने देखा। यह वास्तव में एक अच्छा विचार है। अनिवार्य रूप से लड़का अस्थायी चर में नए जेनरेट किए गए आईडी स्टोर करता है। जो मैं खोज रहा था, उतना ही नहीं (पूरी प्रक्रिया को 1 मर्ज में करने के लिए) लेकिन यह अभी भी एक दिलचस्प दृष्टिकोण है। धन्यवाद मार्टिन। 'अपरिवर्तित' शब्द का उपयोग करने के लिए – George

+0

+1 ... :) –

उत्तर

3

यदि आपके पास टेबलबी पर एक स्वत: जेनरेटेड पीके है, तो आप इस तरह के कोड का उपयोग कर सकते हैं। अन्यथा, पहले टेबलबी से पीके को पकड़ने के लिए INSERT को TableA में बदलें।

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

मुझे लगता है कि यह एक बहुत अच्छा जवाब है; यह <100point पोस्टर धन्यवाद ;-) – George

+0

धन्यवाद, बुरा दिन ... क्षमा करें। – GalacticJello

1

अपने सामान्य प्रश्न का उत्तर देने - 'मैं डालने और में पी वापस लौटा सकते एक बयान जिसे अन्य बयानों में घोंसला दिया जा सकता है '- हाँ, बिल्कुल। लेकिन यह आपके पीके के निर्माण के पीछे तर्क पर निर्भर करता है। इस मामले में, ऐसा लगता है कि एक पीके उत्पन्न करना है, आपको एक अलग टेबल में डालने की आवश्यकता है और फिर वहां से जेनरेट की गई आईडी को पकड़ लें। यह बहुत कुशल नहीं है (आईएमएचओ) जब तक ऐसा करने के लिए एक बहुत ही विशिष्ट कारण नहीं है। Autoincrements, GUIDs, आदि पीके के रूप में बेहतर काम करते हैं। यदि आप इसके पीछे तर्क को सरल/बदल सकते हैं, और आप इसे पूरा करने के लिए एक आसान तरीका पा सकते हैं, तो पीके 'कैन' एक स्टेटमेंट/फ़ंक्शन में उत्पन्न किया जा सकता है और इस प्रकार अन्य कथनों में उपयोग किया जा सकता है।

संबंधित मुद्दे