Manakah yang merupakan opsi terbaik untuk mendapatkan nilai identitas yang baru saja saya hasilkan melalui insert? Apa dampak dari pernyataan-pernyataan ini dari segi performa?
SCOPE_IDENTITY()
MAX()
TOP 1
IdentityColumn FROM TableName ORDER BY IdentityColumn DESC
Gunakan SCOPE_IDENTENTITY()
jika Anda menyisipkan satu baris dan ingin mengambil ID yang dihasilkan.
CREATE TABLE #a(identity_column INT IDENTITY(1,1), x CHAR(1));
INSERT #a(x) VALUES('a');
SELECT SCOPE_IDENTITY();
Hasil:
----
1
Gunakan klausa OUTPUT
jika Anda memasukkan beberapa baris dan perlu mengambil set ID yang dihasilkan.
INSERT #a(x)
OUTPUT inserted.identity_column
VALUES('b'),('c');
Hasil:
----
2
3
dan mengapa ini adalah pilihan terbaik yang lebih cepat?
Selain kinerja, ini adalah satu-satunya yang dijamin benar dalam tingkat isolasi default dan / atau dengan banyak pengguna. Bahkan jika Anda mengabaikan aspek kebenaran, SQL Server menyimpan nilai yang disisipkan di SCOPE_IDENTENTITY ()
dalam memori, jadi tentu saja ini akan lebih cepat daripada menjalankan kueri terisolasi Anda sendiri terhadap tabel atau terhadap tabel sistem.
Mengabaikan aspek kebenaran seperti mengatakan kepada tukang pos bahwa dia melakukan pekerjaan yang baik dalam mengantarkan surat hari ini - dia menyelesaikan rutenya 10 menit lebih cepat dari waktu rata-ratanya, masalahnya adalah, tidak ada surat yang dikirim ke rumah yang tepat.
Jangan menggunakan salah satu dari yang berikut ini:
@@IDENTITY
- karena ini tidak dapat digunakan di semua skenario, misalnya ketika tabel dengan kolom identitas memiliki trigger yang juga menyisipkan ke tabel lain dengan kolom identitasnya sendiri - Anda akan mendapatkan nilai yang salah.IDENT_CURRENT()
- Saya membahas secara detail tentang hal ini di sini, dan komentar-komentarnya juga berguna untuk dibaca, tetapi pada dasarnya, di bawah konkurensi, Anda akan sering mendapatkan jawaban yang salah.MAX()
atau TOP 1
- Anda harus melindungi dua pernyataan dengan isolasi serializable untuk memastikan bahwa MAX()
yang Anda dapatkan bukanlah milik orang lain. Ini jauh lebih mahal daripada hanya menggunakan SCOPE_IDENTITY()
.Fungsi-fungsi ini juga gagal setiap kali Anda menyisipkan dua baris atau lebih, dan membutuhkan semua nilai identitas yang dihasilkan - satu-satunya pilihan Anda di sana adalah klausa OUTPUT
.
Terlepas dari performa, semuanya memiliki makna yang agak berbeda.
SCOPE_IDENTENTITY()
akan memberi Anda nilai identitas terakhir yang dimasukkan ke dalam tabel langsung di dalam ruang lingkup saat ini* (ruang lingkup = batch, prosedur tersimpan, dsb., tetapi tidak di dalam, katakanlah, pemicu yang ditembakkan oleh ruang lingkup saat ini).
IDENT_CURRENT()
akan memberi Anda nilai identitas terakhir yang dimasukkan ke dalam tabel tertentu dari semua ruang lingkup, oleh semua pengguna.
@@IDENTITY
memberi Anda nilai identitas terakhir yang dihasilkan oleh pernyataan INSERT terbaru untuk koneksi saat ini, terlepas dari tabel atau ruang lingkup. (Catatan tambahan: Access menggunakan fungsi ini, dan dengan demikian memiliki beberapa masalah dengan pemicu yang menyisipkan nilai ke dalam tabel dengan kolom identitas).
Menggunakan MAX()
atau TOP 1
dapat memberi Anda hasil yang sepenuhnya salah jika tabel memiliki langkah identitas negatif, atau telah memiliki baris yang disisipkan dengan SET IDENTITY_INSERT
dalam permainan. Berikut ini adalah skrip yang mendemonstrasikan semua ini:
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
Ringkasan: tetap gunakan SCOPE_IDENTENTITY()
, IDENT_CURRENT()
, atau @@IDENTENTITY
, dan pastikan Anda menggunakan salah satu yang mengembalikan apa yang sebenarnya Anda butuhkan.