INSERT OUTPUT yantümcesine güvenmek güvenli midir?


19

Bu tablo verildiğinde:

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

İki farklı senaryoda satır eklemek ve değerleri kimlik sütunundan döndürmek istiyorum.

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

Senaryo 2

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
;

Soru

dbo.Target1) VALUESyan tümcesinde ve 2) #Targettablosunda varolan sırayla döndürülecek tablo ekinden döndürülen kimlik değerlerine güvenebilir miyim , böylece bunları çıktı satır kümesindeki konumlarıyla özgün girdiye göre ilişkilendirebilir miyim?

Referans için

İşte uygulamada neler olduğunu gösteren bazı kesilmiş C # kodu (senaryo 1, yakında kullanıma dönüştürülecek 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();
}

Yanıtlar:


22

1) VALUES yan tümcesinde ve 2) #Target tablosunda bulundukları sırayla döndürülecek olan dbo.Target tablo ekinden döndürülen kimlik değerlerine güvenebilir miyim, böylece bunları çıkış satır kümesindeki konumlarına göre ilişkilendirebilirim Orijinal girişe?

Hayır, gerçek bir belgelenmiş garanti olmadan garanti edilecek hiçbir şeye güvenemezsiniz. Belgelerde böyle bir garantinin olmadığı açıkça belirtiliyor .

SQL Server, OUTPUT deyimi kullanılarak satırların işlenme ve DML ifadeleri tarafından döndürülme sırasını garanti etmez. İstenen anlambilimi garanti edebilecek uygun bir WHERE yantümcesi eklemek veya birden çok satırın DML işlemi için uygun olduğunda, garanti edilen bir sipariş olmadığını anlamak uygulamaya bağlıdır.

Bu, belgelenmemiş birçok varsayımlara dayanacaktır

  1. Satırların sürekli taramadan çıktığı sıra, değerler yan tümcesiyle aynı sıradadır (Onları hiç farklı görmedim ama AFAIK bu garanti edilmez).
  2. Satırların eklenme sırası, sürekli taramadan çıktıkları sırayla aynı olacaktır (kesinlikle her zaman böyle değildir).
  3. Bir "geniş" (dizin başına) yürütme planı kullanılıyorsa, çıktı yan tümcesindeki değerler herhangi bir ikincil dizinden değil, kümelenmiş dizin güncelleştirme işlecinden alınır.
  4. Bundan sonra siparişin korunacağı garanti edilir - örneğin, ambalaj ağ üzerinden iletim için sıralanırken .
  5. Sipariş artık tahmin edilebilir görünse bile, paralel ekleme gibi özelliklerde uygulama değişiklikleri gelecekte sıralamayı değiştirmeyecektir (şu anda sonuçları istemciye döndürmek için INSERT… SELECT deyiminde OUTPUT deyimi belirtilmişse paralel planlar INSERT'ler dahil olmak üzere genel olarak devre dışı bırakıldı )

Cümleye (Color, Action)600 satır eklerseniz , nokta iki başarısızlık örneği (kümelenmiş PK değeri varsayarak ) görülebilir VALUES. Daha sonra plan, eklemeden önce bir sıralama operatörüne sahiptir, böylece VALUESmaddede orijinal siparişinizi kaybedersiniz .

Yine de hedefinize ulaşmanın belgelenmiş bir yolu vardır ve bu, kaynağa bir numaralandırma eklemek ve MERGEbunun yerine kullanmaktır .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; 

resim açıklamasını buraya girin

@İsimsiz bir at

Birleştirme gerçekten gerekli mi? Sadece yapamaz insert into ... select ... from (values (..)) t (...) order by sourceidmısın?

Evet yapabilirdin. SQL Server garantileri Sipariş ... devletler bu

Satırları doldurmak için SELECT ile ORDER BY kullanan sorgular ekleme, kimlik değerlerinin nasıl hesaplandığını garanti eder, ancak satırların eklendiği sırayı değil

Böylece kullanabilirsiniz

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

resim açıklamasını buraya girin

Bu, kimlik değerlerinin t.SourceIdbelirli bir sırada verildikleri sıraya göre atandıklarını, ancak atanmalarını veya atanan kimlik sütunu değerlerinde boşluk bulunmadığını garanti eder (örneğin, eşzamanlı bir ekleme denendiğinde).


2
Boşluk potansiyeli ve çıktının belirli bir sırada olmamasıyla ilgili bu son bit, işleri girdiyle ilişkilendirmeye çalışmak için işleri biraz daha ilginç hale getirir. Uygulamada bir sipariş iş yapmak varsayalım, ama sadece kullanmak için daha güvenli ve daha net görünüyor MERGE.
ErikE

Kullanım OUTPUT ... INTO [#temp]sonra, sözdizimi SELECT ... FROM [#temp] ORDER BYçıkış sırasını garanti etmek.
Max Vernon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.