วิธีที่ดีที่สุดในการIDENTITY
แทรกแถวคืออะไร
ฉันรู้เกี่ยวกับ@@IDENTITY
และIDENT_CURRENT
และSCOPE_IDENTITY
แต่ไม่เข้าใจข้อดีข้อเสียของแต่ละข้อ
ใครช่วยอธิบายความแตกต่างได้และเมื่อใดควรใช้แต่ละข้อ
OUTPUT
คำสั่งใน SQL Server
วิธีที่ดีที่สุดในการIDENTITY
แทรกแถวคืออะไร
ฉันรู้เกี่ยวกับ@@IDENTITY
และIDENT_CURRENT
และSCOPE_IDENTITY
แต่ไม่เข้าใจข้อดีข้อเสียของแต่ละข้อ
ใครช่วยอธิบายความแตกต่างได้และเมื่อใดควรใช้แต่ละข้อ
OUTPUT
คำสั่งใน SQL Server
คำตอบ:
@@IDENTITY
ส่งคืนค่าเอกลักษณ์ล่าสุดที่สร้างขึ้นสำหรับตารางใด ๆ ในเซสชันปัจจุบันข้ามขอบเขตทั้งหมด คุณต้องระวังที่นี่เนื่องจากมันข้ามขอบเขต คุณสามารถรับค่าจากทริกเกอร์แทนที่จะเป็นคำสั่งปัจจุบันของคุณ
SCOPE_IDENTITY()
ส่งคืนค่าเอกลักษณ์ล่าสุดที่สร้างขึ้นสำหรับตารางใด ๆ ในเซสชันปัจจุบันและขอบเขตปัจจุบัน โดยทั่วไปสิ่งที่คุณต้องการใช้
IDENT_CURRENT('tableName')
ส่งคืนค่าเอกลักษณ์ล่าสุดที่สร้างขึ้นสำหรับตารางที่ระบุในเซสชันใด ๆ และขอบเขตใด ๆ สิ่งนี้ช่วยให้คุณระบุตารางที่คุณต้องการค่าในกรณีที่ทั้งสองข้างต้นไม่ได้เป็นสิ่งที่คุณต้องการ ( หายากมาก ) นอกจากนี้ตามที่ @ Guy Starbuckพูดถึง "คุณสามารถใช้สิ่งนี้หากคุณต้องการรับค่าตัวตนปัจจุบันสำหรับตารางที่คุณไม่ได้แทรกบันทึกลงไป"
OUTPUT
ประโยคของINSERT
คำสั่งที่จะช่วยให้คุณเข้าถึงแถวที่ถูกแทรกผ่านคำสั่งที่ทุก เนื่องจากมันถูกกำหนดขอบเขตให้กับคำสั่งเฉพาะจึงตรงไปตรงมามากกว่าฟังก์ชั่นอื่น ๆ ด้านบน อย่างไรก็ตามมันเป็นverbose อีกเล็กน้อย(คุณจะต้องแทรกลงในตัวแปร table / temp table แล้วทำการค้นหา) และให้ผลลัพธ์แม้ในสถานการณ์ข้อผิดพลาดที่คำสั่งถูกย้อนกลับ ที่กล่าวว่าหากแบบสอบถามของคุณใช้แผนการดำเนินการแบบขนานนี่เป็นวิธีเดียวที่รับประกันการรับข้อมูลประจำตัว (ย่อมาจากการปิดแบบขนาน) อย่างไรก็ตามมันจะถูกดำเนินการก่อนทริกเกอร์และไม่สามารถใช้เพื่อส่งกลับค่าที่สร้างทริกเกอร์
output
คุณไม่จำเป็นต้องสร้างตารางชั่วคราวเพื่อจัดเก็บและค้นหาผลลัพธ์ เพียงแค่ละทิ้งinto
ส่วนหนึ่งของส่วนคำสั่งเอาท์พุทและมันจะเอาท์พุทไปยังชุดผลลัพธ์
OUTPUT
คือ "ดีที่สุด" ตราบใดที่คุณไม่ได้ใช้ทริกเกอร์และจัดการข้อผิดพลาด แต่SCOPE_IDENTITY
เป็นปัญหาที่ง่ายที่สุดและไม่ค่อยเกิดขึ้น
ฉันเชื่อว่าวิธีที่ปลอดภัยและแม่นยำที่สุดในการเรียกรหัสที่แทรกไว้จะเป็นการใช้ส่วนคำสั่งเอาท์พุท
ตัวอย่าง (นำมาจากบทความMSDNต่อไปนี้)
USE AdventureWorks2008R2;
GO
DECLARE @MyTableVar table( NewScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO @MyTableVar
VALUES (N'Operator error', GETDATE());
--Display the result set of the table variable.
SELECT NewScrapReasonID, Name, ModifiedDate FROM @MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason;
GO
OUTPUT
ใน SQL Server 2005ดังนั้นดูเหมือนว่าเป็นเพียง SQL Server 2000 และรุ่นก่อนหน้านี้ที่ไม่มี
ฉันกำลังพูดในสิ่งเดียวกันกับคนอื่นดังนั้นทุกคนถูกต้องฉันแค่พยายามทำให้ชัดเจนยิ่งขึ้น
@@IDENTITY
ส่งคืน id ของสิ่งสุดท้ายที่ถูกแทรกโดยการเชื่อมต่อของลูกค้าของคุณไปยังฐานข้อมูล
เวลาส่วนใหญ่ใช้งานได้ดี แต่บางครั้งทริกเกอร์จะไปและแทรกแถวใหม่ที่คุณไม่รู้จักและคุณจะได้รับ ID จากแถวใหม่นี้แทนที่จะเป็นแถวที่คุณต้องการ
SCOPE_IDENTITY()
แก้ปัญหานี้ มันคืนค่า id ของสิ่งสุดท้ายที่คุณใส่ในรหัส SQL ที่คุณส่งไปยังฐานข้อมูล หากทริกเกอร์ไปและสร้างแถวเพิ่มเติมพวกเขาจะไม่ทำให้ค่าผิดกลับคืนมา ไชโย
IDENT_CURRENT
ส่งคืน ID ล่าสุดที่ถูกแทรกโดยทุกคน หากมีแอปอื่นแทรกแถวอื่นในเวลาที่ไม่คาดคิดคุณจะได้รับ ID ของแถวนั้นแทนแอพของคุณ
SCOPE_IDENTITY()
ถ้าคุณต้องการที่จะเล่นที่ปลอดภัยใช้เสมอ หากคุณติดกับ@@IDENTITY
ใครบางคนตัดสินใจที่จะเพิ่มทริกเกอร์ในภายหลังรหัสของคุณทั้งหมดจะแตก
วิธีที่ดีที่สุด (อ่าน: ปลอดภัยที่สุด) ในการรับข้อมูลประจำตัวของแถวที่เพิ่งแทรกใหม่คือการใช้ส่วนoutput
คำสั่ง:
create table TableWithIdentity
( IdentityColumnName int identity(1, 1) not null primary key,
... )
-- type of this table's column must match the type of the
-- identity column of the table you'll be inserting into
declare @IdentityOutput table ( ID int )
insert TableWithIdentity
( ... )
output inserted.IdentityColumnName into @IdentityOutput
values
( ... )
select @IdentityValue = (select ID from @IdentityOutput)
scope_identity()
) ในการรับแผนคู่ขนาน และข้อผิดพลาดนี้ได้รับการแก้ไขมากกว่าหนึ่งปีก่อนคำตอบนี้
output
scope_identity()
ฉันลบ FUD เกี่ยวกับการรวมกลุ่มในคำตอบ
เพิ่ม
SELECT CAST(scope_identity() AS int);
ที่ส่วนท้ายของคำสั่งแทรก sql ของคุณจากนั้น
NewId = command.ExecuteScalar()
จะดึงมันมา
เมื่อคุณใช้ Entity Framework ภายในจะใช้OUTPUT
เทคนิคเพื่อส่งคืนค่า ID ที่แทรกใหม่
DECLARE @generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID ]
FROM @generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE @@ROWCOUNT > 0
ผลลัพธ์ของผลลัพธ์จะถูกเก็บไว้ในตัวแปรตารางชั่วคราวเข้าร่วมกลับไปที่ตารางและส่งกลับค่าแถวออกจากตาราง
หมายเหตุ: ฉันไม่รู้ว่าทำไม EF จะเข้าร่วมตารางชั่วคราวกลับไปที่ตารางจริง (ทั้งสองสถานการณ์จะไม่ตรงกัน)
แต่นั่นคือสิ่งที่ EF ทำ
เทคนิคนี้ ( OUTPUT
) มีเฉพาะใน SQL Server 2008 หรือใหม่กว่าเท่านั้น
เหตุผลที่ Entity Framework เชื่อมโยงกลับไปที่ตารางดั้งเดิมแทนที่จะใช้OUTPUT
ค่าเพียงเพราะ EF ยังใช้เทคนิคนี้เพื่อรับrowversion
แถวที่แทรกใหม่
คุณสามารถใช้การเห็นพ้องในแง่ดีในโมเดลเอนทิตี้ของเฟรมเวิร์กโดยใช้แอTimestamp
ททริบิว: 🕗
public class TurboEncabulator
{
public String StatorSlots)
[Timestamp]
public byte[] RowVersion { get; set; }
}
เมื่อคุณทำเช่นนี้ Entity Framework จะต้องการrowversion
แถวที่แทรกใหม่:
DECLARE @generated_keys table([Id] uniqueidentifier)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID INTO @generated_keys
VALUES('Malleable logarithmic casing');
SELECT t.[TurboEncabulatorID], t.[RowVersion]
FROM @generated_keys AS g
JOIN dbo.TurboEncabulators AS t
ON g.Id = t.TurboEncabulatorID
WHERE @@ROWCOUNT > 0
และเพื่อที่จะดึงข้อมูลนี้Timetsamp
คุณไม่สามารถใช้ส่วนOUTPUT
คำสั่งได้
นั่นเป็นเพราะหากมีการเรียกบนโต๊ะใด ๆ ที่Timestamp
คุณส่งออกจะผิด:
การประทับเวลาที่ส่งคืนจะไม่ถูกต้องหากคุณมีทริกเกอร์บนตาราง ดังนั้นคุณจะต้องSELECT
ใช้แยกต่างหาก
และแม้ว่าคุณจะยินดีที่จะได้รับความถูกต้อง rowversion เหตุผลอื่นในการดำเนินการแยกต่างหากSELECT
คือคุณไม่สามารถส่งrowversion
ออกเป็นตัวแปรตาราง:
DECLARE @generated_keys table([Id] uniqueidentifier, [Rowversion] timestamp)
INSERT INTO TurboEncabulators(StatorSlots)
OUTPUT inserted.TurboEncabulatorID, inserted.Rowversion INTO @generated_keys
VALUES('Malleable logarithmic casing');
เหตุผลที่สามที่ต้องทำเพื่อความสมมาตร เมื่อทำการแสดงUPDATE
บนโต๊ะที่มีทริกเกอร์คุณจะไม่สามารถใช้OUTPUT
ประโยคได้ การลองทำUPDATE
ด้วย an OUTPUT
ไม่ได้รับการสนับสนุนและจะให้ข้อผิดพลาด:
วิธีเดียวที่จะทำได้คือใช้SELECT
คำสั่งติดตามผล:
UPDATE TurboEncabulators
SET StatorSlots = 'Lotus-O deltoid type'
WHERE ((TurboEncabulatorID = 1) AND (RowVersion = 792))
SELECT RowVersion
FROM TurboEncabulators
WHERE @@ROWCOUNT > 0 AND TurboEncabulatorID = 1
TurboEncabulators
:) ของคุณ
@@ IDENTITY, SCOPE_IDENTITY และ IDENT_CURRENT เป็นฟังก์ชั่นที่คล้ายกันซึ่งจะส่งคืนค่าสุดท้ายที่แทรกเข้าไปในคอลัมน์ IDENTITY ของตาราง
@@ IDENTITY และ SCOPE_IDENTITY จะส่งกลับค่าตัวตนสุดท้ายที่สร้างในตารางใด ๆ ในเซสชันปัจจุบัน อย่างไรก็ตาม SCOPE_IDENTITY ส่งคืนค่าภายในขอบเขตปัจจุบันเท่านั้น @@ IDENTITY ไม่ จำกัด เฉพาะขอบเขต
IDENT_CURRENT ไม่ได้ถูก จำกัด โดยขอบเขตและเซสชัน มันถูก จำกัด ไว้ที่ตารางที่ระบุ IDENT_CURRENT ส่งคืนค่าเอกลักษณ์ที่สร้างขึ้นสำหรับตารางเฉพาะในเซสชันใด ๆ และขอบเขตใด ๆ สำหรับข้อมูลเพิ่มเติมโปรดดู IDENT_CURRENT
@@ IDENTITYเป็นข้อมูลประจำตัวสุดท้ายที่แทรกโดยใช้การเชื่อมต่อ SQL ปัจจุบัน นี่เป็นค่าที่ดีในการกลับมาจากขั้นตอนการจัดเก็บแทรกซึ่งคุณเพียงแค่ต้องใส่ข้อมูลประจำตัวสำหรับบันทึกใหม่ของคุณและไม่สนใจว่าจะมีการเพิ่มแถวเพิ่มเติมหลังจากนั้น
SCOPE_IDENTITYเป็นข้อมูลประจำตัวสุดท้ายที่แทรกโดยใช้การเชื่อมต่อ SQL ปัจจุบันและในขอบเขตปัจจุบัน - นั่นคือถ้ามีการระบุตัวตนที่สองขึ้นอยู่กับทริกเกอร์หลังจากแทรกของคุณมันจะไม่ปรากฏใน SCOPE_IDENTITY เฉพาะแทรกที่คุณดำเนินการ . ตรงไปตรงมาฉันไม่เคยมีเหตุผลที่จะใช้สิ่งนี้
IDENT_CURRENT (tablename)เป็นข้อมูลประจำตัวที่แทรกล่าสุดโดยไม่คำนึงถึงการเชื่อมต่อหรือขอบเขต คุณสามารถใช้สิ่งนี้หากคุณต้องการรับค่าตัวตนปัจจุบันสำหรับตารางที่คุณยังไม่ได้แทรกบันทึก
ฉันไม่สามารถพูดคุยกับ SQL Server รุ่นอื่น ๆ ได้ แต่ในปี 2012 การแสดงผลออกมาใช้งานได้ดี คุณไม่จำเป็นต้องกังวลกับตารางชั่วคราว
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES (...)
โดยวิธีการเทคนิคนี้ยังใช้งานได้เมื่อแทรกหลายแถว
INSERT INTO MyTable
OUTPUT INSERTED.ID
VALUES
(...),
(...),
(...)
เอาท์พุต
ID
2
3
4
OUTPUT
แต่จุดของฉันก็คือว่ามันไม่ได้เป็นความต้องการของ หากคุณไม่ต้องการตาราง temp การค้นหาของคุณจะจบลงง่ายกว่าเดิม
ใช้ scope_identity () เสมอไม่จำเป็นต้องมีสิ่งใดเลย
สร้างuuid
และยังแทรกลงในคอลัมน์ จากนั้นคุณสามารถระบุแถวของคุณได้อย่างง่ายดายด้วย uuid นั่นเป็นโซลูชั่นการทำงานเพียง 100% ที่คุณสามารถนำไปใช้ได้ โซลูชันอื่นทั้งหมดนั้นซับซ้อนเกินไปหรือใช้งานไม่ได้ในกรณีขอบเดียวกัน เช่น:
1) สร้างแถว
INSERT INTO table (uuid, name, street, zip)
VALUES ('2f802845-447b-4caa-8783-2086a0a8d437', 'Peter', 'Mainstreet 7', '88888');
2) สร้างแถว
SELECT * FROM table WHERE uuid='2f802845-447b-4caa-8783-2086a0a8d437';
uuid
ในฐานข้อมูล ดังนั้นแถวจะพบได้เร็วขึ้น
https://www.npmjs.com/package/uuid
นี้: const uuidv4 = require('uuid/v4'); const uuid = uuidv4()
อีกวิธีหนึ่งที่จะรับประกันตัวตนของแถวที่คุณแทรกคือการระบุค่าตัวตนและใช้แล้วSET IDENTITY_INSERT ON
OFF
สิ่งนี้รับประกันว่าคุณจะรู้ว่าคุณค่าของตัวตนคืออะไร! ตราบใดที่ค่าไม่ได้ใช้งานคุณสามารถแทรกค่าเหล่านี้ลงในคอลัมน์ข้อมูลประจำตัว
CREATE TABLE #foo
(
fooid INT IDENTITY NOT NULL,
fooname VARCHAR(20)
)
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (1,
'one'),
(2,
'Two')
SET IDENTITY_INSERT #foo OFF
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
INSERT INTO #foo
(fooname)
VALUES ('Three')
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
-- YOU CAN INSERT
SET IDENTITY_INSERT #foo ON
INSERT INTO #foo
(fooid,
fooname)
VALUES (10,
'Ten'),
(11,
'Eleven')
SET IDENTITY_INSERT #foo OFF
SELECT @@Identity AS [@@Identity],
Scope_identity() AS [SCOPE_IDENTITY()],
Ident_current('#Foo') AS [IDENT_CURRENT]
SELECT *
FROM #foo
นี่เป็นเทคนิคที่มีประโยชน์มากถ้าคุณกำลังโหลดข้อมูลจากแหล่งอื่นหรือรวมข้อมูลจากฐานข้อมูลสองแห่งเป็นต้น
แม้จะเป็นหัวข้อเก่ามีวิธีใหม่ที่จะทำนี้ที่หลีกเลี่ยงบางส่วนของข้อผิดพลาดของรหัสประจำตัวคอลัมน์ในรุ่นเก่าของ SQL Server, เช่นช่องว่างในค่าตัวตนหลังจากรีบูตเซิร์ฟเวอร์ ลำดับที่มีอยู่ใน SQL Server 2016 และส่งต่อซึ่งเป็นวิธีที่ใหม่กว่าคือการสร้างวัตถุลำดับโดยใช้ TSQL สิ่งนี้ช่วยให้คุณสร้างวัตถุลำดับตัวเลขของคุณเองใน SQL Server และควบคุมวิธีการเพิ่มขึ้น
นี่คือตัวอย่าง:
CREATE SEQUENCE CountBy1
START WITH 1
INCREMENT BY 1 ;
GO
จากนั้นใน TSQL คุณจะทำสิ่งต่อไปนี้เพื่อรับ ID ลำดับถัดไป:
SELECT NEXT VALUE FOR CountBy1 AS SequenceID
GO
นี่คือลิงค์สำหรับสร้างลำดับและมูลค่าถัดไปสำหรับ
หลังจากคำสั่งแทรกของคุณคุณจะต้องเพิ่มนี้ และตรวจสอบให้แน่ใจเกี่ยวกับชื่อตารางที่มีการแทรกข้อมูลคุณจะได้รับแถวปัจจุบันไม่มีตำแหน่งที่ได้รับผลกระทบจากคำสั่งแทรกของคุณ
IDENT_CURRENT('tableName')
INSERT INTO Table1(fields...) OUTPUT INSERTED.id VALUES (...)
หรือวิธีที่เก่ากว่า:INSERT INTO Table1(fields...) VALUES (...); SELECT SCOPE_IDENTITY();
คุณสามารถรับมันใน c # โดยใช้ ExecuteScalar ()