क्या INSERT के OUTPUT क्लॉज के आदेश पर भरोसा करना सुरक्षित है?


19

इस तालिका को देखते हुए:

CREATE TABLE dbo.Target (
   TargetId int identity(1, 1) NOT NULL,
   Color varchar(20) NOT NULL,
   Action varchar(10) NOT NULL, -- of course this should be normalized
   Code int NOT NULL,
   CONSTRAINT PK_Target PRIMARY KEY CLUSTERED (TargetId)
);

दो अलग-अलग परिदृश्यों में मैं पंक्तियों को सम्मिलित करना चाहता हूं और पहचान कॉलम से मान लौटाता हूं।

दृष्टांत 1

INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM
   (VALUES
      ('Blue', 'New', 1234),
      ('Blue', 'Cancel', 4567),
      ('Red', 'New', 5678)
   ) t (Color, Action, Code)
;

दृश्य २

CREATE TABLE #Target (
   Color varchar(20) NOT NULL,
   Action varchar(10) NOT NULL,
   Code int NOT NULL,
   PRIMARY KEY CLUSTERED (Color, Action)
);

-- Bulk insert to the table the same three rows as above by any means

INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM #Target
;

सवाल

क्या मैं dbo.Target1) VALUESक्लॉज और 2) #Targetटेबल में मौजूद ऑर्डर में वापस आने के लिए टेबल से लौटे पहचान मूल्यों पर भरोसा कर सकता हूं , ताकि मैं उन्हें मूल इनपुट पर वापस आउटपुट आउटपुटसेट में अपनी स्थिति से सहसंबंधित कर सकूं?

सन्दर्भ के लिए

यहां कुछ ट्रिम किए गए C # कोड हैं जो प्रदर्शित करते हैं कि एप्लिकेशन में क्या हो रहा है (परिदृश्य 1, जल्द ही इसका उपयोग करने के लिए परिवर्तित किया जाएगा SqlBulkCopy):

public IReadOnlyCollection<Target> InsertTargets(IEnumerable<Target> targets) {
   var targetList = targets.ToList();
   const string insertSql = @"
      INSERT dbo.Target (
         CoreItemId,
         TargetDateTimeUtc,
         TargetTypeId,
      )
      OUTPUT
         Inserted.TargetId
      SELECT
         input.CoreItemId,
         input.TargetDateTimeUtc,
         input.TargetTypeId,
      FROM
         (VALUES
            {0}
         ) input (
            CoreItemId,
            TargetDateTimeUtc,
            TargetTypeId
         );";
   var results = Connection.Query<DbTargetInsertResult>(
      string.Format(
         insertSql,
         string.Join(
            ", ",
            targetList
               .Select(target => $@"({target.CoreItemId
                  }, '{target.TargetDateTimeUtc:yyyy-MM-ddTHH:mm:ss.fff
                  }', {(byte) target.TargetType
                  })";
               )
         )
      )
      .ToList();
   return targetList
      .Zip( // The correlation that relies on the order of the two inputs being the same
         results,
         (inputTarget, insertResult) => new Target(
            insertResult.TargetId, // with the new TargetId to replace null.
            inputTarget.TargetDateTimeUtc,
            inputTarget.CoreItemId,
            inputTarget.TargetType
         )
      )
      .ToList()
      .AsReadOnly();
}

जवाबों:


22

क्या मैं dbo.Target टेबल से लौटे पहचान मूल्यों पर भरोसा कर सकता हूं ताकि वे 1) वैल्यूज़ क्लॉज़ और 2) #Target टेबल में मौजूद क्रम में वापस आ सकें, ताकि मैं उन्हें आउटपुट रोसेट में अपनी स्थिति से सहसंबंधित कर सकूं मूल इनपुट के लिए

नहीं, आप वास्तविक दस्तावेज की गारंटी के बिना किसी भी चीज़ पर भरोसा नहीं कर सकते। दस्तावेज़ में स्पष्ट रूप से कहा गया है कि ऐसी कोई गारंटी नहीं है।

SQL सर्वर उस आदेश की गारंटी नहीं देता है जिसमें पंक्तियों को संसाधित किया जाता है और OUTPUT क्लॉज का उपयोग करके DML कथनों द्वारा लौटाया जाता है। यह एक उचित WHERE क्लॉज को शामिल करने के लिए आवेदन पर निर्भर है जो वांछित शब्दार्थों की गारंटी दे सकता है, या यह समझ सकता है कि जब कई पंक्तियाँ DML ऑपरेशन के लिए योग्य हो सकती हैं, तो कोई गारंटी आदेश नहीं है।

यह बहुत सी अनिर्धारित मान्यताओं पर निर्भर करेगा

  1. निरंतर स्कैन से पंक्तियाँ जिस क्रम में आउटपुट होती हैं, वह मान खंड के रूप में उसी क्रम में है (मैंने उन्हें कभी अलग नहीं देखा है लेकिन AFAIK यह गारंटी नहीं है)।
  2. पंक्तियों को जिस क्रम में डाला जाता है, वह उसी क्रम के समान होगा जो वे निरंतर स्कैन से आउटपुट होते हैं (निश्चित रूप से हमेशा मामला नहीं)।
  3. यदि एक "विस्तृत" (प्रति सूचकांक) निष्पादन योजना का उपयोग करते हुए आउटपुट क्लॉज से मूल्यों को क्लस्टर इंडेक्स अपडेट ऑपरेटर से निकाला जाएगा और किसी भी द्वितीयक इंडेक्स का नहीं।
  4. इस आदेश को उसके बाद संरक्षित करने की गारंटी दी जाती है - उदाहरण के लिए जब पैकेजिंग नेटवर्क पर संचरण के लिए पंक्तियाँ बनाती है
  5. भले ही यह आदेश पूर्वानुमेय प्रतीत होता है, लेकिन समानांतर इंसर्ट जैसी सुविधाओं में क्रियान्वयन परिवर्तन भविष्य में ऑर्डर नहीं बदलेगा (वर्तमान में यदि OUTPUT क्लॉज INSERT में निर्दिष्ट किया गया है ... क्लाइंट को परिणाम वापस करने के लिए कथन का चयन करें, तो समानांतर योजनाएं हैं INSERT सहित सामान्य रूप से अक्षम )

(Color, Action)यदि आप VALUESखंड में 600 पंक्तियाँ जोड़ते हैं, तो पॉइंट टू फेलिंग (क्लस्टर पीके मानकर ) का उदाहरण देखा जा सकता है । तब योजना को सम्मिलित करने से पहले एक सॉर्ट ऑपरेटर होता है ताकि VALUESक्लॉज में अपना मूल आदेश खो जाए ।

हालांकि अपने लक्ष्य को प्राप्त करने का एक प्रलेखित तरीका है और यह स्रोत के MERGEबजाय एक नंबर जोड़ना है और इसके बजाय उपयोग करना हैINSERT

MERGE dbo.Target
USING (VALUES (1, 'Blue', 'New', 1234),
              (2, 'Blue', 'Cancel', 4567),
              (3, 'Red', 'New', 5678) ) t (SourceId, Color, Action, Code)
ON 1 = 0
WHEN NOT MATCHED THEN
  INSERT (Color,
          Action,
          Code)
  VALUES (Color,
          Action,
          Code)
OUTPUT t.SourceId,
       inserted.TargetId; 

यहाँ छवि विवरण दर्ज करें

@बिना नाम का घोड़ा

क्या मर्ज वास्तव में आवश्यक है? तुम सिर्फ एक नहीं कर सकता है insert into ... select ... from (values (..)) t (...) order by sourceid?

हाँ तुम कर सकते हो। SQL सर्वर में गारंटी देने का आदेश ... कहता है कि

पंक्तियों को पॉप्युलेट करने के लिए ORDER BY के साथ सेलेक्ट करने वाले INSERT प्रश्न यह गारंटी देते हैं कि पहचान मूल्यों की गणना कैसे की जाती है लेकिन उस क्रम को नहीं जिसमें पंक्तियाँ डाली जाती हैं।

तो आप उपयोग कर सकते हैं

INSERT dbo.Target (Color, Action, Code)
OUTPUT inserted.TargetId
SELECT t.Color, t.Action, t.Code
FROM
(VALUES (1, 'Blue', 'New', 1234),
        (2, 'Blue', 'Cancel', 4567),
        (3, 'Red', 'New', 5678) ) t (SourceId, Color, Action, Code)
ORDER BY t.SourceId

यहाँ छवि विवरण दर्ज करें

यह गारंटी देता है कि पहचान मूल्यों को क्रम में सौंपा गया है, t.SourceIdलेकिन यह नहीं कि वे किसी विशेष क्रम में आउटपुट हैं या यह कि पहचान किए गए स्तंभ मानों में कोई अंतराल नहीं है (जैसे कि समवर्ती सम्मिलित प्रयास किया जाता है)।


2
अंतराल के लिए क्षमता और किसी विशेष क्रम में आउटपुट नहीं होने के बारे में यह अंतिम बात है कि इनपुट में वापस सहसंबंध बनाने की कोशिश करने के लिए चीजें थोड़ी अधिक दिलचस्प हैं। मुझे लगता है कि आवेदन में एक आदेश काम करेगा, लेकिन यह सुरक्षित और स्पष्ट लगता है कि बस का उपयोग करें MERGE
ErikE

OUTPUT ... INTO [#temp]सिंटैक्स का उपयोग करें , फिर SELECT ... FROM [#temp] ORDER BYआउटपुट ऑर्डर की गारंटी देने के लिए।
मैक्स वर्नोन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.