SQL Server - Nilai pengembalian setelah INSERT

318

Saya mencoba untuk mendapatkan kembali nilai kunci setelah pernyataan INSERT. Contoh: Saya punya tabel dengan nama atribut dan id. id adalah nilai yang dihasilkan.

    INSERT INTO table (name) VALUES('bob');

Sekarang saya ingin mendapatkan id kembali di langkah yang sama. Bagaimana ini dilakukan?

Kami menggunakan Microsoft SQL Server 2008.

melbic
sumber
Saya menemukan jawaban yang berguna di sini: [tombol-pernyataan-siap-dengan-kembali-dihasilkan-kunci] [1] [1]: stackoverflow.com/questions/4224228/…
Lars Ladegaard
1
Kemungkinan duplikat dari Cara terbaik untuk mendapatkan identitas dari baris yang dimasukkan?
Vladimir Vagaytsev

Jawaban:

477

Tidak perlu untuk PILIH yang terpisah ...

INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');

Ini juga berfungsi untuk kolom non-IDENTITAS (seperti GUID)

gbn
sumber
29
bisakah kamu menguraikan sedikit? Kemana perginya Output dalam contoh ini? Dokumentasi hanya menunjukkan contoh untuk tabel (menggunakan output ... ke). Idealnya saya hanya ingin meneruskannya ke variabel
JonnyRaa
2
@JonnyLeeds: Anda tidak dapat melakukannya ke variabel (kecuali variabel tabel). OUTPUT pergi ke klien atau meja
gbn
7
Sayangnya, Anda tidak dapat mengandalkan ini karena menambahkan pemicu ke tabel akan merusak pernyataan Anda! re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/...
hajikelist
1
@hajikelist: ini kasus tepi, SET NCOOUNT AKTIF dalam pelatuk biasanya membantu. Lihat stackoverflow.com/questions/1483732/set-nocount-on-usage
gbn
5
Jangan pernah gunakan @@ IDENTITY. SCOPE_IDENTITY, ya, tetapi tidak pernah @@ IDENTITY. Itu tidak bisa diandalkan
gbn
188

Gunakan SCOPE_IDENTITY()untuk mendapatkan nilai ID baru

INSERT INTO table (name) VALUES('bob');

SELECT SCOPE_IDENTITY()

http://msdn.microsoft.com/en-us/library/ms190315.aspx

Singkat
sumber
7
dengan asumsi ididentitas
Ilia G
12
@ liho1eye - OP menyebut nama kolom identitas sebagai id, jadi ya.
Curt
3
Pada sistem yang lebih besar, bagaimana jika banyak sql dijalankan pada waktu yang bersamaan? Apakah akan mengembalikan id yang terakhir disisipkan ke setiap permintaan?
Shiv
2
@Shiv "SCOPE_IDENTITY mengembalikan nilai yang dimasukkan hanya dalam lingkup saat ini"
goodies4uall
45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

Adalah taruhan teraman karena ada masalah yang diketahui dengan konflik Klausa OUTPUT pada tabel dengan pemicu. Membuat ini sangat tidak dapat diandalkan karena bahkan jika meja Anda saat ini tidak memiliki pemicu - seseorang menambahkan satu di telepon akan merusak aplikasi Anda. Bom waktu semacam perilaku.

Lihat artikel msdn untuk penjelasan lebih lanjut:

http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx

hajikelist
sumber
Hanya jika Anda tidak menambahkan SET NOCOUNT ON di pemicu. Juga lihat docs.microsoft.com/en-us/sql/database-engine/configure-windows/…
gbn
ini bukan opsi untuk lingkungan lawas kami @gbn
hajikelist
@hajikelist Kita semua memiliki warisan, tetapi risiko pemicu mengacaukan OUTPUT rendah, yang diperlukan hanyalah mengaktifkan nocount. Jika seseorang menambahkan pemicu maka mereka harus tahu bagaimana kode itu (menyiratkan Anda memiliki kontrol terutama), atau Anda perlu melatih pengembang Anda .. Pada titik tertentu, Anda akan dipaksa untuk bermigrasi ketika versi SQL tidak lagi didukung dll sehingga pemicu tidak akan menyebabkan resultset. Apa pun, itu bukan jawaban terbaik karena jika Anda memiliki BUKAN pemicu SCOPE_IDENTITY mungkin tidak berfungsi ( stackoverflow.com/questions/908257/… )
gbn
@ GBN - Saya hanya ingin menghindari hal-hal konyol seperti ini. Saya tidak akan memberi tahu semua pengembang saya, "Jangan lupa menambahkan 'jangan hancurkan pernyataan aplikasi' di setiap pemicu." - kamu bisa menyimpannya. Skenario "bukannya" jauh lebih dari kasus tepi imo.
hajikelist
Jawaban yang lebih aman mungkin hanya agar aplikasi menjalankan kueri lain setelah kembali dari yang ini. Selama itu dilakukan di bagian belakang, penalti kinerja harus sebanding dengan kesederhanaan mengelola pengembangan dalam kelompok orang, dan itu lebih dekat sesuai dengan standar daripada fitur gila dengan kasus tepi. Saya lebih suka kasus tepi berada di kode saya, dan menghindarinya di platform. hanya pendapat saya jangan panik :)
Dan Chase
33

Entity Framework melakukan sesuatu yang mirip dengan jawaban gbn:

DECLARE @generated_keys table([Id] uniqueidentifier)

INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO @generated_keys
VALUES('bob');

SELECT t.[CustomerID]
FROM @generated_keys AS g 
   JOIN dbo.Customers AS t 
   ON g.Id = t.CustomerID
WHERE @@ROWCOUNT > 0

Hasil output disimpan dalam variabel tabel sementara, dan kemudian dipilih kembali ke klien. Harus mewaspadai gotcha:

sisipan dapat menghasilkan lebih dari satu baris, sehingga variabel dapat menampung lebih dari satu baris, sehingga Anda dapat dikembalikan lebih dari satu baris ID

Saya tidak tahu mengapa EF akan bergabung dengan tabel ephemeral kembali ke meja nyata (dalam keadaan apa keduanya tidak cocok).

Tapi itulah yang dilakukan EF.

SQL Server 2008 atau lebih baru. Jika ini 2005 maka Anda kurang beruntung.

Ian Boyd
sumber
2
Alasan EF melakukannya adalah untuk memastikan itu juga "melihat" semua perubahan lain pada Customercatatan yang dimasukkan , karena mungkin ada logika sisi DB lainnya yang memengaruhinya, misalnya DEFAULTpada beberapa kolom, pemicu pada tabel, dll. EF memperbarui entitas (objek) yang digunakan untuk menyisipkan, sehingga sisi klien mendapatkan objek pelanggan dengan ID dan segala sesuatu yang mewakili status baris saat ini.
Hilarion
Alasan lain untuk tidak menggunakan EF.
cskwg
11

@@IDENTITY Merupakan fungsi sistem yang mengembalikan nilai identitas yang dimasukkan terakhir.

AngelaG
sumber
4
Harus menyarankan agar jangan pernah menggunakan @@ IDENTITY - tidak akurat (terlalu luas) tidak terlalu aman - silakan lihat jawaban @ Curt tentang SCOPE_IDENTITY ().
zanlok
10

Ada banyak cara untuk keluar setelah dimasukkan

Saat Anda memasukkan data ke dalam tabel, Anda bisa menggunakan klausa OUTPUT untuk mengembalikan salinan data yang telah dimasukkan ke dalam tabel. Klausa OUTPUT mengambil dua bentuk dasar: OUTPUT dan OUTPUT KE. Gunakan formulir OUTPUT jika Anda ingin mengembalikan data ke aplikasi panggilan. Gunakan formulir OUTPUT INTO jika Anda ingin mengembalikan data ke tabel atau variabel tabel.

DECLARE @MyTableVar TABLE (id INT,NAME NVARCHAR(50));

INSERT INTO tableName
(
  NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO @MyTableVar
VALUES
(
   'test',...
)

IDENT_CURRENT : Ini mengembalikan identitas terakhir yang dibuat untuk tabel atau tampilan tertentu dalam sesi apa pun.

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : Ini mengembalikan identitas terakhir dari sesi yang sama dan cakupan yang sama. Ruang lingkup adalah prosedur / pemicu tersimpan dll.

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITY : Ini mengembalikan identitas terakhir dari sesi yang sama.

SELECT @@IDENTITY AS [@@IDENTITY];
Reza Jenabi
sumber
1
@RezaJenabi Jun, out put sangat bagus, lebih baik daripada menemukan banyak id di tabel. Aku digunakan out putuntuk bulk insertdan insert oleh select statement. terima kasih atas saran Anda
Amirhossein
5

Solusi terbaik dan paling pasti adalah menggunakan SCOPE_IDENTITY() .

Hanya Anda harus mendapatkan identitas lingkup setelah setiap sisipan dan menyimpannya dalam variabel karena Anda dapat memanggil dua sisipan dalam cakupan yang sama.

ident_currentdan @@identitymungkin mereka bekerja tetapi mereka bukan ruang lingkup yang aman. Anda dapat memiliki masalah dalam aplikasi besar

  declare @duplicataId int
  select @duplicataId =   (SELECT SCOPE_IDENTITY())

Lebih detail ada di sini Microsoft docs

MNF
sumber
1
Dapat menyederhanakan ini keselect @duplicataId = SCOPE_IDENTITY()
pcnate
1
OUTPUTklausa adalah solusi yang lebih baik, lebih murni :)
Dale K
OUTPUT INTO sangat lambat.
cskwg
4

Anda dapat menggunakan scope_identity()untuk memilih ID dari baris yang baru saja Anda masukkan ke dalam variabel kemudian pilih kolom apa pun yang Anda inginkan dari tabel di mana id = identitas yang Anda dapatkan dariscope_identity()

Lihat di sini untuk info MSDN http://msdn.microsoft.com/en-us/library/ms190315.aspx

Ikan purplegold
sumber
1

Ada beberapa cara untuk mendapatkan ID yang dimasukkan terakhir setelah memasukkan perintah.

  1. @@IDENTITY : Ini mengembalikan nilai Identitas terakhir yang dihasilkan pada Koneksi di sesi saat ini, terlepas dari Tabel dan ruang lingkup pernyataan yang menghasilkan nilai
  2. SCOPE_IDENTITY(): Ini mengembalikan nilai identitas terakhir yang dihasilkan oleh pernyataan insert di lingkup saat ini di koneksi saat ini terlepas dari tabel.
  3. IDENT_CURRENT(‘TABLENAME’): Ini mengembalikan nilai identitas terakhir yang dihasilkan pada tabel yang ditentukan terlepas dari koneksi, sesi, atau ruang lingkup. IDENT_CURRENT tidak dibatasi oleh ruang lingkup dan sesi; terbatas pada tabel yang ditentukan.

Sekarang tampaknya lebih sulit untuk memutuskan mana yang akan cocok dengan kebutuhan saya.

Saya lebih suka SCOPE_IDENTITY ().

Jika Anda menggunakan pilih SCOPE_IDENTITY () bersama dengan TableName dalam pernyataan insert, Anda akan mendapatkan hasil yang tepat sesuai harapan Anda.

Sumber: CodoBee

Ishrar
sumber
0

Ini adalah bagaimana saya menggunakan OUTPUT INSERTED, ketika menyisipkan ke tabel yang menggunakan ID sebagai kolom identitas di SQL Server:

'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)
Richard Zembron
sumber
0

Anda dapat menambahkan pernyataan pilih ke pernyataan sisipan Anda. Integer myInt = Masukkan nilai table1 (FName) ('Fred'); Pilih Scope_Identity (); Ini akan mengembalikan nilai identitas ketika dieksekusi scaler.

Robert Quinn
sumber
-4

Setelah melakukan penyisipan ke tabel dengan kolom identitas, Anda dapat mereferensikan @@ IDENTITY untuk mendapatkan nilai: http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx

FanOfTamago
sumber
24
Jangan pernah gunakan @@ IDENTITY: ini bukan lingkup aman: memicu dll dan mempengaruhinya.
gbn
-4

* Urutan parameter dalam string koneksi terkadang penting. * Lokasi parameter Penyedia dapat mematahkan kursor recordset setelah menambahkan baris. Kami melihat perilaku ini dengan penyedia SQLOLEDB.

Setelah baris ditambahkan, bidang baris tidak tersedia, KECUALI Penyedia ditentukan sebagai parameter pertama dalam string koneksi. Ketika penyedia mana pun dalam string koneksi kecuali sebagai parameter pertama, bidang baris yang baru dimasukkan tidak tersedia. Ketika kami memindahkan Penyedia ke parameter pertama, bidang baris secara ajaib muncul.

David Guidos
sumber
1
Bisakah Anda memberi tahu kami bagaimana komentar ini menjawab / relevan dengan pertanyaan yang diajukan? Saya tidak merasa pantas menggunakan topi / tebal. Jika jawaban Anda dianggap membantu, pengguna akan memberikan suara.
n__o
Banyak pengguna mungkin datang ke halaman ini karena mereka tidak memiliki bidang yang valid untuk mengidentifikasi baris yang baru saja ditambahkan. Perilaku ini kami temukan (yang hanya mengubah urutan parameter dalam string koneksi memungkinkan mengakses baris yang baru ditambahkan dengan segera) sangat aneh sehingga saya pikir pantas disebutkan dalam huruf besar, terutama karena sangat mungkin akan memperbaiki alasan bahwa orang menginginkan yang baru ID baris dan bidang lain dari baris itu. Dengan hanya menempatkan penyedia sebagai parameter pertama, masalahnya hilang.
David Guidos
Anda perlu mengedit dan meningkatkan jawaban Anda. Saat ini berisik dan tidak tampil sebagai jawaban yang layak atau bahkan upaya
James
Apa sebenarnya yang Anda maksud dengan "berisik"? Anda perlu menjelaskan keluhan Anda. Ini sesederhana mungkin. Jika Anda mengubah urutan parameter dalam string koneksi Anda, ini dapat memengaruhi apakah data baris tersedia setelah disisipkan.
David Guidos