SQL Server - คืนค่าหลัง INSERT


318

ฉันพยายามรับคีย์ - ค่ากลับมาหลังจากคำสั่ง INSERT ตัวอย่าง: ฉันมีตารางพร้อมชื่อแอตทริบิวต์และรหัส id เป็นค่าที่สร้างขึ้น

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

ตอนนี้ฉันต้องการรับรหัสกลับมาในขั้นตอนเดียวกัน มันจะทำอย่างไร

เรากำลังใช้ Microsoft SQL Server 2008


ฉันพบคำตอบที่เป็นประโยชน์ได้ที่นี่: [เตรียมพร้อมสถานะ - กับ - คำสั่ง - สร้าง - คืน - คีย์] [1] [1]: stackoverflow.com/questions/4224228/…
Lars Ladegaard

คำตอบ:


477

ไม่จำเป็นต้องเลือก SELECT ...

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

ใช้งานได้กับคอลัมน์ที่ไม่ใช่ตัวตน (เช่น GUID)


29
คุณอธิบายรายละเอียดเล็กน้อยได้ไหม? ผลลัพธ์ไปในตัวอย่างนี้ที่ไหน เอกสารประกอบแสดงตัวอย่างสำหรับตารางเท่านั้น (โดยใช้เอาต์พุต ... เข้า) เป็นการดีที่ฉันต้องการเพียงแค่สามารถส่งผ่านเข้าไปในตัวแปร
JonnyRaa

2
@JonnyLeeds: คุณไม่สามารถทำได้กับตัวแปร (ยกเว้นตัวแปรตาราง) การส่งออกไปยังลูกค้าหรือโต๊ะ
GBN

7
น่าเสียดายที่คุณไม่สามารถไว้วางใจสิ่งนี้ได้เนื่องจากการเพิ่มทริกเกอร์ในตารางจะทำให้คำสั่งของคุณแตก! Re: blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/…
hajikelist

1
@hajikelist: นี่เป็นกรณีที่ค่อนข้างขอบ NCOOUNT เปิดในทริกเกอร์มักจะช่วย ดูstackoverflow.com/questions/1483732/set-nocount-on-usage
gbn

5
อย่าใช้ @@ ตัวระบุ SCOPE_IDENTITY ใช่ แต่ไม่เคย @@ IDENTITY มันจะไม่น่าเชื่อถือ
GBN

188

ใช้SCOPE_IDENTITY()เพื่อรับค่า ID ใหม่

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

SELECT SCOPE_IDENTITY()

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


7
สมมติว่าidเป็นตัวตน
Ilia G

12
@ liho1eye - OP อ้างถึงชื่อคอลัมน์ข้อมูลประจำตัวidดังนั้นใช่
ห้วน

3
บนระบบที่ใหญ่กว่าจะเกิดอะไรขึ้นถ้า sql จำนวนมากทำงานพร้อมกัน? มันจะส่งคืนรหัสที่แทรกล่าสุดไปยังทุกคำขอหรือไม่
ชีฟ

2
@Shiv "SCOPE_IDENTITY ส่งคืนค่าที่แทรกภายในขอบเขตปัจจุบันเท่านั้น"
goodies4uall

45
INSERT INTO files (title) VALUES ('whatever'); 
SELECT * FROM files WHERE id = SCOPE_IDENTITY();

เป็นการเดิมพันที่ปลอดภัยที่สุดเนื่องจากมีปัญหาที่ทราบเกี่ยวกับ OUTPUT Clause ขัดแย้งบนตารางที่มีทริกเกอร์ ทำให้สิ่งนี้ไม่น่าเชื่อถือแม้ว่าตารางของคุณจะไม่มีทริกเกอร์ใด ๆ ในขณะนี้ แต่บางคนที่เพิ่มหนึ่งบรรทัดจะทำให้แอปพลิเคชันของคุณพัง การจัดเรียงของ Time Bomb

ดูบทความ msdn สำหรับคำอธิบายที่ลึกกว่า:

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


เฉพาะเมื่อคุณไม่เพิ่ม SET NOCOUNT ON ในทริกเกอร์ โปรดดูdocs.microsoft.com/en-us/sql/database-engine/configure-windows/?hl=th
gbn

นี่ไม่ใช่ตัวเลือกสำหรับสภาพแวดล้อมแบบดั้งเดิมของเรา @gbn
hajikelist

@hajikelist เราทุกคนมีมรดก แต่ความเสี่ยงในการทริกเกอร์เอาต์เอาท์พุทอัพอยู่ในระดับต่ำสิ่งที่ต้องทำก็คือตั้งค่า nocount หากมีใครบางคนเพิ่มทริกเกอร์พวกเขาควรรู้วิธีเขียนโค้ด (บอกเป็นนัยว่าคุณสามารถควบคุมได้เป็นหลัก) หรือคุณต้องการฝึกอบรมนักพัฒนาของคุณ .. ในบางจุดคุณจะถูกบังคับให้ย้ายเมื่อ SQL รุ่นนั้นไม่อยู่อีกต่อไป รองรับและอื่น ๆ ดังนั้นทริกเกอร์จะไม่ก่อให้เกิดชุดผลลัพธ์ อะไรก็ตามมันไม่ใช่คำตอบที่ดีที่สุดเพราะถ้าคุณมี INSTEAD จากทริกเกอร์ SCOPE_IDENTITY อาจไม่ทำงาน ( stackoverflow.com/questions/908257/… )
gbn

@gbn - ฉันแค่ชอบหลีกเลี่ยงสิ่งที่โง่ ๆ แบบนี้ ฉันจะไม่บอกนักพัฒนาทั้งหมดของฉัน "อย่าลืมเพิ่ม" อย่าทำลายคำสั่งแอปของฉัน "ในทุก ๆ การเรียก" - คุณสามารถเก็บมันไว้ สถานการณ์ "แทนที่จะ" เป็นเรื่องของกรณีขอบมากกว่า
hajikelist

คำตอบที่ปลอดภัยอาจเป็นเพียงเพื่อให้แอปพลิเคชันเรียกใช้แบบสอบถามอื่นเมื่อส่งคืนจากแบบสอบถามนี้ ตราบใดที่มีการทำในส่วนหลังการลงโทษควรมีค่ากับความเรียบง่ายในการจัดการการพัฒนาในกลุ่มคนและมันใกล้เคียงกับมาตรฐานมากกว่าฟีเจอร์ที่บ้าคลั่งกับเคสแบบขอบ ฉันต้องการที่จะเป็นกรณีขอบในรหัสของฉันและหลีกเลี่ยงพวกเขาบนแพลตฟอร์ม เพียงแค่ความคิดของฉันไม่ออกนอกลู่นอกทาง :)
แดนเชส

33

Entity Framework ทำสิ่งที่คล้ายกับคำตอบของ 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

ผลลัพธ์ของผลลัพธ์จะถูกเก็บไว้ในตัวแปรตารางชั่วคราวจากนั้นเลือกกลับไปยังไคลเอนต์ ต้องระวัง gotcha:

ส่วนแทรกสามารถสร้างได้มากกว่าหนึ่งแถวดังนั้นตัวแปรสามารถเก็บได้มากกว่าหนึ่งแถวดังนั้นคุณสามารถส่งคืนได้มากกว่าหนึ่งแถว ID

ฉันไม่รู้ว่าทำไม EF ถึงจะเข้าร่วมตารางชั่วคราวกลับไปที่ตารางจริง (ทั้งสองสถานการณ์จะไม่ตรงกัน)

แต่นั่นคือสิ่งที่ EF ทำ

SQL Server 2008 หรือใหม่กว่าเท่านั้น ถ้าเป็นปี 2005 แสดงว่าคุณโชคไม่ดี


2
เหตุผลที่ EF ทำขึ้นเพื่อให้แน่ใจว่าสามารถ "ดู" การเปลี่ยนแปลงอื่น ๆ ทั้งหมดในCustomerเรคคอร์ดที่แทรกเนื่องจากอาจมีตรรกะด้าน DB อื่น ๆ ที่มีผลกระทบกับมันเช่นDEFAULTในบางคอลัมน์ทริกเกอร์บนตาราง ฯลฯ EF จะอัพเดต เอนทิตี (object) ที่ใช้สำหรับการแทรกดังนั้นฝั่งไคลเอ็นต์จะได้รับออบเจ็กต์ลูกค้าที่มี ID และทุกอย่างที่แสดงสถานะปัจจุบันของแถว
Hilarion

อีกเหตุผลที่ไม่ควรใช้ EF
cskwg

11

@@IDENTITY เป็นฟังก์ชันของระบบที่ส่งคืนค่าข้อมูลประจำตัวที่แทรกล่าสุด


4
ต้องให้คำแนะนำกับการใช้ @@ ประจำตัว - ไม่ถูกต้อง (กว้างเกินไป) ปลอดภัยน้อยกว่าด้ายมาก - โปรดดู @ คำตอบของ Curt เกี่ยวกับ SCOPE_IDENTITY ()
zanlok

10

มีหลายวิธีในการออกหลังจากแทรก

เมื่อคุณแทรกข้อมูลลงในตารางคุณสามารถใช้ส่วนคำสั่ง OUTPUT เพื่อส่งคืนสำเนาของข้อมูลที่ถูกแทรกลงในตาราง ส่วนคำสั่ง OUTPUT ใช้สองรูปแบบพื้นฐาน: OUTPUT และ OUTPUT INTO ใช้แบบฟอร์ม OUTPUT หากคุณต้องการส่งคืนข้อมูลไปยังแอปพลิเคชันการโทร ใช้แบบฟอร์ม OUTPUT INTO ถ้าคุณต้องการส่งคืนข้อมูลไปยังตารางหรือตัวแปรตาราง

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

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

IDENT_CURRENT : มันส่งกลับข้อมูลประจำตัวล่าสุดที่สร้างขึ้นสำหรับตารางหรือมุมมองเฉพาะในเซสชันใด ๆ

SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]

SCOPE_IDENTITY : มันส่งกลับข้อมูลประจำตัวล่าสุดจากเซสชั่นเดียวกันและขอบเขตเดียวกัน ขอบเขตคือกระบวนงาน / ทริกเกอร์ที่เก็บไว้ ฯลฯ

SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];  

@@ IDENTITY : มันส่งกลับตัวตนล่าสุดจากเซสชั่นเดียวกัน

SELECT @@IDENTITY AS [@@IDENTITY];

1
@RezaJenabi มิ.ย. งานวางได้ผลดีมากดีกว่าหาไอดีจำนวนมากในตาราง ฉันใช้out putสำหรับและแทรกโดยbulk insert select statementขอบคุณคำแนะนำของคุณ
Amirhossein

5

ทางออกที่ดีที่สุดและแน่นอนที่สุดคือการใช้ SCOPE_IDENTITY()ที่ดีที่สุดและแน่ใจว่าส่วนใหญ่วิธีการแก้ปัญหาคือการใช้

เพียงแค่คุณต้องรับรหัสประจำตัวของขอบเขตหลังจากทุกการแทรกและบันทึกในตัวแปรเนื่องจากคุณสามารถเรียกการแทรกสองครั้งในขอบเขตเดียวกัน

ident_currentและ@@identityอาจเป็นไปได้ว่าทำงานได้ แต่ไม่ปลอดภัย คุณสามารถมีปัญหาในแอปพลิเคชันขนาดใหญ่

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

รายละเอียดเพิ่มเติมอยู่ที่นี่เอกสารของ Microsoft


1
สามารถทำให้สิ่งนี้ง่ายขึ้นselect @duplicataId = SCOPE_IDENTITY()
pcnate

1
OUTPUTclause เป็นการแก้ปัญหาที่ดีกว่าและบริสุทธิ์กว่า :)
Dale K

ผลลัพธ์เข้าช้ามาก
cskwg

4

คุณสามารถใช้ได้ scope_identity()เพื่อเลือก ID ของแถวที่คุณเพิ่งแทรกเข้าไปในตัวแปรจากนั้นเลือกคอลัมน์ใดก็ได้ที่คุณต้องการจากตารางที่ id = ตัวตนที่คุณได้รับจากscope_identity()

ดูที่นี่สำหรับข้อมูล MSDN http://msdn.microsoft.com/en-us/library/ms190315.aspx


1

มีหลายวิธีในการรับ ID ที่แทรกล่าสุดหลังจากคำสั่ง insert

  1. @@IDENTITY : มันจะส่งกลับค่าตัวตนล่าสุดที่สร้างขึ้นในการเชื่อมต่อในเซสชันปัจจุบันโดยไม่คำนึงถึงตารางและขอบเขตของคำสั่งที่สร้างค่า
  2. SCOPE_IDENTITY(): มันคืนค่าตัวตนสุดท้ายที่สร้างโดยคำสั่งแทรกในขอบเขตปัจจุบันในการเชื่อมต่อปัจจุบันโดยไม่คำนึงถึงตาราง
  3. IDENT_CURRENT(‘TABLENAME’): มันคืนค่าตัวตนสุดท้ายที่สร้างขึ้นในตารางที่ระบุโดยไม่คำนึงถึงการเชื่อมต่อใด ๆ เซสชั่นหรือขอบเขต IDENT_CURRENT ไม่ได้ถูก จำกัด โดยขอบเขตและเซสชัน มันถูก จำกัด ไว้ที่ตารางที่ระบุ

ตอนนี้ดูเหมือนว่าจะยากกว่าที่จะตัดสินใจว่าอันไหนจะตรงกับความต้องการของฉัน

ฉันชอบ SCOPE_IDENTITY () เป็นส่วนใหญ่

หากคุณใช้เลือก SCOPE_IDENTITY () พร้อมกับ TableName ในคำสั่งแทรกคุณจะได้รับผลลัพธ์ที่แน่นอนตามความคาดหมายของคุณ

ที่มา: CodoBee


0

นี่คือวิธีที่ฉันใช้ OUTPUT INSERTED เมื่อแทรกเข้าไปในตารางที่ใช้ ID เป็นคอลัมน์ข้อมูลประจำตัวใน 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)

0

คุณสามารถผนวกคำสั่ง select เข้ากับคำสั่งแทรกของคุณ จำนวนเต็ม myInt = แทรกลงในค่า table1 (FName) ('Fred'); เลือก Scope_Identity (); สิ่งนี้จะคืนค่าของข้อมูลประจำตัวเมื่อดำเนินการตัวคัดกรอง


-4

หลังจากทำการแทรกลงในตารางด้วยคอลัมน์ข้อมูลประจำตัวคุณสามารถอ้างอิง @@ IDENTITY เพื่อรับค่า: http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx


24
อย่าใช้ @@ IDENTITY: มันไม่ได้อยู่ในขอบเขตที่ปลอดภัย: ทริกเกอร์และผลกระทบอื่น ๆ
gbn

-4

* ลำดับพารามิเตอร์ในสตริงการเชื่อมต่อบางครั้งมีความสำคัญ *ตำแหน่งของพารามิเตอร์ผู้ให้บริการสามารถแบ่งเคอร์เซอร์ชุดระเบียนหลังจากเพิ่มแถว เราเห็นพฤติกรรมนี้กับผู้ให้บริการ SQLOLEDB

หลังจากเพิ่มแถวแล้วฟิลด์แถวจะไม่พร้อมใช้งานเว้นแต่ผู้ให้บริการจะระบุเป็นพารามิเตอร์แรกในสตริงการเชื่อมต่อ เมื่อผู้ให้บริการอยู่ที่ใดก็ได้ในสตริงการเชื่อมต่อยกเว้นเป็นพารามิเตอร์แรกเขตข้อมูลแถวที่แทรกใหม่จะไม่พร้อมใช้งาน เมื่อเราย้ายผู้ให้บริการไปที่พารามิเตอร์แรกเขตข้อมูลแถวจะปรากฏขึ้นอย่างน่าอัศจรรย์


1
คุณสามารถบอกเราได้ว่าความคิดเห็นนี้ตอบอย่างไร / เกี่ยวข้องกับคำถามที่ถาม? ฉันไม่รู้สึกว่าสมควรได้รับตัวพิมพ์ใหญ่ / ตัวหนา หากคำตอบของคุณมีประโยชน์ผู้ใช้จะลงคะแนนให้
n__o

ผู้ใช้จำนวนมากอาจมาที่หน้านี้เพราะไม่มีฟิลด์ที่ถูกต้องในการระบุแถวที่เพิ่งเพิ่ม พฤติกรรมนี้เราพบ (ที่เพียงแค่เปลี่ยนลำดับของพารามิเตอร์ในสตริงการเชื่อมต่อช่วยให้เข้าถึงแถวที่เพิ่งเพิ่มใหม่ได้ทันที) เป็นสิ่งที่แปลกมากที่ฉันคิดว่ามันสมควรได้รับการกล่าวถึงในหมวกโดยเฉพาะอย่างยิ่งเพราะมันน่าจะแก้ไขเหตุผลว่า ID แถวและฟิลด์อื่น ๆ ของแถวนั้น เพียงใส่ผู้ให้บริการเป็นพารามิเตอร์แรกปัญหาจะหายไป
David Guidos

คุณต้องแก้ไขและปรับปรุงคำตอบของคุณ ขณะนี้มันมีเสียงดังและไม่เจอคำตอบที่ดีหรือแม้แต่ความพยายาม
James

"noisy" หมายความว่าอะไร? คุณต้องอธิบายคำร้องเรียนของคุณ มันเป็นเรื่องง่ายที่สุดเท่าที่จะทำได้ หากคุณเปลี่ยนลำดับของพารามิเตอร์ในสตริงการเชื่อมต่อของคุณอาจมีผลต่อข้อมูลแถวที่มีอยู่หลังจากการแทรก
David Guidos
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.