วิธีที่ดีที่สุดในการแทรกข้อมูลประจำตัวสุดท้ายในตาราง


35

ตัวเลือกใดเป็นตัวเลือกที่ดีที่สุดในการรับค่าตัวตนที่ฉันเพิ่งสร้างผ่านการแทรก อะไรคือผลกระทบของแถลงการณ์เหล่านี้ในแง่ของประสิทธิภาพ?

  1. SCOPE_IDENTITY()
  2. ฟังก์ชันการรวม MAX()
  3. SELECT TOP 1IdentityColumn จาก TableNameORDER BY IdentityColumn DESC

1
ใช้ postgreSQL และคุณจะได้รับจากชั้นวางpostgresql.org/docs/9.1/static/sql-insert.html
Yevgeniy Afanasyev

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

คำตอบ:


56

ใช้SCOPE_IDENTITY()หากคุณกำลังแทรกแถวเดียวและต้องการดึง ID ที่ถูกสร้างขึ้น

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

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

SELECT SCOPE_IDENTITY();

ผล:

----
1

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

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

ผล:

----
2
3

และทำไมตัวเลือกนี้ถึงเร็วที่สุด

นอกเหนือจากประสิทธิภาพแล้วสิ่งเหล่านี้เป็นสิ่งเดียวที่รับประกันได้ว่าถูกต้องในระดับการแยกเริ่มต้นและ / หรือมีผู้ใช้หลายคน แม้ว่าคุณจะเพิกเฉยต่อความถูกต้อง แต่ SQL Server ยังคงมีค่าที่แทรกอยู่SCOPE_IDENTITY()ในหน่วยความจำดังนั้นโดยปกติแล้วสิ่งนี้จะเร็วกว่าการไปและเรียกใช้แบบสอบถามแบบแยกเดี่ยวของคุณกับตารางหรือจากตารางระบบ

การเพิกเฉยต่อความถูกต้องนั้นก็เหมือนกับการบอกพนักงานส่งจดหมายว่าเขาทำงานได้ดีในการส่งจดหมายของวันนี้ - เขาเสร็จสิ้นเส้นทางของเขาเร็วกว่าเวลาเฉลี่ย 10 นาทีปัญหาคือไม่มีจดหมายใดถูกส่งไปยังบ้านที่ถูกต้อง

อย่าใช้สิ่งใดสิ่งหนึ่งต่อไปนี้:

  • @@IDENTITY - เนื่องจากสิ่งนี้ไม่สามารถใช้ได้ในทุกสถานการณ์ตัวอย่างเช่นเมื่อตารางที่มีคอลัมน์ข้อมูลประจำตัวมีทริกเกอร์ที่แทรกเข้าไปในตารางอื่นด้วยคอลัมน์ข้อมูลประจำตัวของมันเอง - คุณจะได้รับค่าที่ไม่ถูกต้องกลับมา
  • IDENT_CURRENT()- ฉันได้ลงรายละเอียดเกี่ยวกับเรื่องนี้ที่นี่และความเห็นก็เป็นประโยชน์สำหรับการอ่านเช่นกัน แต่โดยพื้นฐานแล้วคุณจะได้รับคำตอบที่ผิด
  • MAX()หรือTOP 1- คุณจะต้องปกป้องทั้งสองข้อความด้วยการแยกแบบแยกได้เพื่อให้แน่ใจว่าMAX()คุณได้รับไม่ใช่คนอื่น SCOPE_IDENTITY()นี่คือราคาแพงกว่าเพียงแค่ใช้

ฟังก์ชั่นเหล่านี้ล้มเหลวเมื่อใดก็ตามที่คุณแทรกสองแถวขึ้นไปและต้องการค่าเอกลักษณ์ทั้งหมดที่สร้างขึ้น - ตัวเลือกเดียวของคุณคือส่วนOUTPUTคำสั่ง


อีกหนึ่งคำถามที่ถูกกระตุ้นในความทรงจำของฉัน เมื่อใดที่เราจำเป็นต้องได้รับข้อมูลประจำตัวสุดท้ายที่สร้างขึ้นในตารางที่ระบุไม่ว่าจะโดยเซสชันหรือผู้ใช้ใด ๆ วิธีที่ถูกต้องและดีที่สุดคือ MAX () ของคอลัมน์นั้น
AA.SC

เมื่อฉันแทรกหลายแถวในตาราง SCOPE_IDENTITY () จะส่งกลับข้อมูลประจำตัวที่สร้างขึ้นล่าสุดได้อย่างไร จะเกิดอะไรขึ้นถ้าคอลัมน์เป็นคีย์หลัก แต่ไม่ใช่คอลัมน์ข้อมูลประจำตัว
AA.SC

@ AA.SC ใช่มันจะคืนค่าล่าสุด หากไม่ใช่คอลัมน์ข้อมูลประจำตัวจะไม่มีฟังก์ชั่นเหล่านี้ใช้งานได้ ค่า PK มาจากไหนในกรณีนี้
Aaron Bertrand

ฉันได้เห็นสิ่งนี้สำหรับคอลัมน์ในแอปพลิเคชันของเราที่คอลัมน์เป็นประเภท INT และนักพัฒนาใช้ MAX (ชื่อคอลัมน์) +1 เมื่อพวกเขาต้องการแทรกระเบียนใหม่
AA.SC

จากนั้นพวกเขาก็รู้แล้วว่าพวกเขาใส่คุณค่าอะไรลงไป SQL Server ไม่มีวิธีใดที่จะบอกคุณว่า (คุณไม่สามารถวางใจได้ว่าจะดึง MAX อีกครั้งหลังจากข้อเท็จจริงเว้นแต่ว่าคุณแยกธุรกรรมทั้งหมดของคุณอย่างสมบูรณ์ซึ่งจะไม่ดีต่อประสิทธิภาพหรือการทำงานพร้อมกัน)
Aaron Bertrand

7

นอกเหนือจากการแสดงแล้วพวกเขาทั้งหมดมีความหมายแตกต่างกันมาก

SCOPE_IDENTITY()จะให้คุณระบุค่าตัวตนสุดท้ายลงในตารางใด ๆโดยตรงภายในขอบเขตปัจจุบัน (scope = batch, กระบวนงานที่เก็บไว้, ฯลฯ แต่ไม่ได้อยู่ภายใน, พูด, ทริกเกอร์ที่ถูกไล่ออกจากขอบเขตปัจจุบัน)

IDENT_CURRENT()จะให้ค่าตัวตนสุดท้ายแทรกลงในตารางที่เฉพาะเจาะจงจากใด ๆขอบเขตโดยใด ๆของผู้ใช้

@@IDENTITYให้ค่าตัวตนล่าสุดที่สร้างโดยคำสั่ง INSERT ล่าสุดสำหรับการเชื่อมต่อปัจจุบันโดยไม่คำนึงถึงตารางหรือขอบเขต (หมายเหตุด้าน: Access ใช้ฟังก์ชันนี้และมีปัญหาบางอย่างเกี่ยวกับทริกเกอร์ที่แทรกค่าลงในตารางที่มีคอลัมน์ข้อมูลประจำตัว)

การใช้MAX()หรือTOP 1สามารถให้ผลลัพธ์ที่ผิดทั้งหมดถ้าตารางมีขั้นตอนการลบข้อมูลประจำตัวหรือมีการแทรกแถวด้วยSET IDENTITY_INSERTในการเล่น นี่เป็นสคริปต์ที่แสดงให้เห็นถึงสิ่งเหล่านี้:

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

สรุป: ติดกับSCOPE_IDENTITY(), IDENT_CURRENT()หรือ@@IDENTITY, และตรวจสอบให้แน่ใจว่าคุณกำลังใช้อันที่คืนสิ่งที่คุณต้องการจริง ๆ


1
เหตุใดคุณจึงสนับสนุนให้ใช้IDENT_CURRENT()และ@@IDENTITYเมื่อสคริปต์ของคุณแสดงให้เห็นว่าพวกเขาแสดงผลลัพธ์ที่ไม่ถูกต้อง
Aaron Bertrand

1
@AaronBertrand ฉันไม่แน่ใจว่าฉันติดตาม ค่าตัวตนสุดท้ายที่สร้างขึ้นคือ 8998 (สังเกตว่าขั้นตอนคือ -1) และนั่นคือสิ่งที่IDENT_CURRENT()ส่งคืน MAX () ไม่เคยส่งกลับค่าสิทธิเกินแถวแรกตั้งแต่ ID เป็นถอยหลังนับและด้วยความIDENTITY_INSERTที่ 9005 ไม่ได้เป็นที่สร้างIDENT_CURRENT()ค่าตัวตนจึงไม่ได้สะท้อนจาก แต่มันอาจส่งคืนผลลัพธ์ที่ "ไม่ถูกต้อง" หากคุณเป็นคนจริงหลังจากสิ่งที่SCOPE_IDENTITY()ส่งคืนแล้ว เลือกเครื่องมือที่เหมาะสมสำหรับงาน
db2

OP น่าจะเป็นหลังจากค่าตัวตนที่พวกเขาแทรก - ในกรณีนี้ 8998 ไม่ถูกต้อง กรณีขอบที่คุณพูดถึง (การเพิ่มขึ้นหลังและ IDENTITY_INSERT บน) ยิ่งโต้แย้งกับการใช้ IDENT_CURRENT ในความคิดของฉันและ @@ IDENTITY ไม่ควรนำมาใช้เพราะอันตรายจากทริกเกอร์ (ตอนนี้หรือเพิ่มในภายหลัง) ฉันยังคงดิ้นรนที่จะเข้าใจว่าทำไม IDENT_CURRENT จึงเป็น OP ที่ต้องการใช้ (โดยเฉพาะอย่างยิ่งภายใต้การทำงานพร้อมกัน) หรือเหตุใด @@ IDENTITY จึงถูกใช้โดยทุกคนเมื่อมีวิธีการที่เชื่อถือได้มากกว่า
Aaron Bertrand

@AaronBertrand ไม่ชัดเจน 100% จากคำถามที่ผลลัพธ์ที่ต้องการคือการแทรกครั้งล่าสุดจากขอบเขตปัจจุบัน (ตัวเลือก 1 แตกต่างจาก 2 และ 3 ในเรื่องนั้น) ดังนั้นฉันคิดว่ามันเป็นความคิดที่ดีที่จะอธิบายทั้งสองและวิธีการที่พวกเขา แตกต่าง แต่ฉันเห็นด้วยที่@@IDENTITYเกือบจะไม่เคยเป็นวิธีที่เหมาะที่จะได้รับค่าตัวตนที่สร้างขึ้น ประเด็นหลักคือMAX()หรือTOP 1เป็นเวอร์ชั่นที่น่าเชื่อถือน้อยกว่าIDENT_CURRENT()ซึ่งเป็นฟังก์ชั่นที่สมบูรณ์แบบที่จะใช้หากคุณเข้าใจในสิ่งที่มันทำ อาจเป็นประโยชน์สำหรับงานบำรุงรักษาหรือบางสิ่งบางอย่าง
db2
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.