Bir tabloya en son kimliği eklemenin en iyi yolu


35

Eklemeyle az önce oluşturduğum kimlik değerini almak için en iyi seçenek hangisidir? Bu ifadelerin performans açısından etkisi nedir?

  1. SCOPE_IDENTITY()
  2. Toplama işlevi MAX()
  3. TOP 1TableName from SELECT IdentityColumnORDER BY IdentityColumn DESC


Sol alan seçeneği - tabloda bir Kılavuz sütunu varsa ve yeni bir Kılavuz oluşturabilir ve ekleme sırasında yeni sütuna ekleyebilirseniz - oluşturulan int kimliğini çıkarmak için bu Kılavuz ile ilgili satırı seçebilirsiniz.
niico

Yanıtlar:


56

KullanımSCOPE_IDENTITY() tek bir satır ekleme ve oluşturulan kimliğini almak istiyorum eğer.

CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));

INSERT #a(x) VALUES('a');

SELECT SCOPE_IDENTITY();

Sonuç:

----
1

OUTPUTBirden çok satır ekliyorsanız ve oluşturulan kimlik grubunu almanız gerekiyorsa, bu cümleyi kullanın .

INSERT #a(x) 
  OUTPUT inserted.identity_column 
  VALUES('b'),('c');

Sonuç:

----
2
3

ve neden bu en hızlı seçenek?

Performans bir yana, bunlar varsayılan yalıtım seviyesinde ve / veya birden fazla kullanıcıyla doğru olması garanti edilenler. Doğruluk özelliğini görmezden gelseniz bile, SQL Server eklenen değeri SCOPE_IDENTITY()bellekte tutar , bu nedenle doğal olarak bu, kendi yalıtılmış sorgunuzu masaya veya sistem tablolarına karşı çalıştırmaktan ve çalıştırmaktan daha hızlı olacaktır.

Doğruluğu göz ardı etmek postacıya bugünün postasını teslim etmek için iyi bir iş yaptığını söylemek gibidir - rotasını ortalama süresinden 10 dakika daha hızlı bitirdi, sorun şu ki, postaların hiçbiri doğru eve teslim edilmedi.

Aşağıdakilerden hiçbirini kullanmayın:

  • @@IDENTITY - Bu, tüm senaryolarda kullanılamayacağından, örneğin bir kimlik sütunu içeren bir masa, kendi kimlik sütunu olan başka bir tabloya da ekleyen bir tetikleyiciye sahip olduğunda - yanlış değeri geri alırsınız.
  • IDENT_CURRENT()- Burada bununla ilgili ayrıntılara giriyorum ve yorumlar da okumakta fayda var, ancak esasen, eşzamanlılık altında, sık sık yanlış cevap alırsınız.
  • MAX()veya TOP 1- Aldığınızın MAX()başkasının olmadığından emin olmak için iki ifadeyi seri hale getirilebilir yalıtımlı olarak korumak zorundasınız . Bu sadece kullanmaktan çok daha pahalıdır SCOPE_IDENTITY().

Bu işlevler iki veya daha fazla satır eklediğinizde de başarısız olur ve üretilen tüm kimlik değerlerine ihtiyaç duyar - tek seçeneğiniz OUTPUTyan tümcedir.


Hafızamda bir soru daha tetiklendi. Herhangi bir oturum veya kullanıcı tarafından belirli bir tabloda en son oluşturulan kimliği almamız gerektiğinde, bu sütunun MAX () en doğru ve en iyi yolu nedir?
AA.SC,

bir tabloya birden çok satır eklediğimde SCOPE_IDENTITY () her zaman en son oluşturulan kimliği döndürür? Sütun birincil Anahtar ancak Kimlik sütunu değilse?
AA.SC

@ AA.SC evet, sonuncusunu döndürecektir. Bir kimlik sütunu değilse, hayır, bu işlevlerin hiçbiri çalışmaz. Bu durumda PK değeri nereden geliyor?
Aaron Bertrand

Bunu,
kolonumuzun

O zaman zaten hangi değeri eklediklerini biliyorlar. SQL Server size bunu söylemenin hiçbir yolu yoktur (işleminizin tamamını tamamen izole etmediğiniz sürece performanstan veya eşzamanlılıktan faydalanmayacaksanız, MAX'i tekrar çekmeye güvenemezsiniz).
Aaron Bertrand

7

Performans dışında, hepsinin oldukça farklı anlamları vardır.

SCOPE_IDENTITY()Herhangi bir tabloya doğrudan geçerli kapsam dahiline eklenen son kimlik değerini (kapsam = toplu iş, saklı yordam vb., ancak geçerli kapsam tarafından başlatılan bir tetikleyici dahilinde değil) verir.

IDENT_CURRENT()Herhangi bir kapsamdan, herhangi bir kullanıcı tarafından, belirli bir tabloya eklenen son kimlik değerini size verecektir .

@@IDENTITYtablo veya kapsamdan bağımsız olarak mevcut bağlantı için en son INSERT ifadesi tarafından oluşturulan son kimlik değerini verir. (Not: Erişim bu işlevi kullanır ve böylece kimlik sütunlarına sahip tablolara değer ekleyen tetikleyicilerle ilgili bazı sorunlar vardır.)

Tablonun negatif bir kimlik adımı varsa veya oyuna eklenmiş satırlar varsa, kullanmak MAX()veya TOP 1size tamamen yanlış sonuçlar verebilir SET IDENTITY_INSERT. İşte bunların hepsini gösteren bir senaryo:

CREATE TABLE ReverseIdent (
    id int IDENTITY(9000,-1) NOT NULL PRIMARY KEY CLUSTERED,
    data char(4)
)

INSERT INTO ReverseIdent (data)
VALUES ('a'), ('b'), ('c')

SELECT * FROM ReverseIdent

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9000

SET IDENTITY_INSERT ReverseIdent ON

INSERT INTO ReverseIdent (id, data)
VALUES (9005, 'd')

SET IDENTITY_INSERT ReverseIdent OFF

SELECT IDENT_CURRENT('ReverseIdent') --8998
SELECT MAX(id) FROM ReverseIdent --9005

Özet: ile sopa SCOPE_IDENTITY(), IDENT_CURRENT()ya da @@IDENTITY, ve emin olun bir tane döner ne gerçekte ihtiyaç olduğunu kullanıyoruz.


1
Neden IDENT_CURRENT()ve @@IDENTITYkendi senaryonuzun yanlış sonuçlar verdiğini gösterdiğinde kullanımını teşvik ediyorsunuz.
Aaron Bertrand

1
@AaronBertrand Takip ettiğimden emin değilim. Üretilen son kimlik değeri 8998'di (adımın -1 olduğuna dikkat edin) ve IDENT_CURRENT()döndürülen budur. MAX () hiçbir zaman doğru değeri ilk satırın ötesinde döndürmez, çünkü id geriye doğru sayılır ve IDENTITY_INSERTon ile 9005 oluşturulan bir kimlik değeri değildir, bu nedenle yansıtılmaz IDENT_CURRENT(). Ancak , gerçekte neyin peşindeyseniz, "yanlış" sonuçlara neden olabilirSCOPE_IDENTITY() . İş için doğru aracı seçin.
db2

OP ekledikleri kimlik değerinin peşinde gibi görünüyor - bu durumda 8998 yanlış. Bahsettiğiniz son davalar (geriye doğru artış ve IDENTITY_INSERT açık) benim görüşüme göre IDENT_CURRENT kullanımına karşı daha fazla tartışıyorlar ve @@ IDENTITY tetikleyiciler tehlikesi nedeniyle hiç kullanılmamalı (şimdi veya daha sonra eklendi). IDENT_CURRENT'in neden OP'nin kullanmak istediği (özellikle eşzamanlılık altında) olacağını ya da neden @@ IDENTITY'nin çok daha güvenilir yöntemler varken hiç kimse tarafından kullanılacağını anlamakta zorlanıyorum.
Aaron Bertrand

@AaronBertrand İstenilen sonucun mevcut kapsamdaki son seçenek olduğu sorusundan% 100 net değil (seçenek 1 bu konuda 2 ve 3'ten farklı), bu yüzden hem nasıl hem de nasıl tanımlayacağının iyi bir fikir olacağını düşündüm. farklılık. Ancak kabul ediyorum ki @@IDENTITY, bu kimlik oluşturmada neredeyse hiçbir zaman ideal bir yöntem değildir. Asıl mesele, onun ne yaptığını anlarsanız, kullanmak için mükemmel bir fonksiyon olan, daha az güvenilir bir versiyonudur MAX()veya TOP 1gibidir IDENT_CURRENT(). Bakım işleri veya başka şeyler için faydalı olabilir.
db2
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.