Cách tốt nhất để có được danh tính cuối cùng được chèn vào bảng


35

Cái nào là tùy chọn tốt nhất để lấy giá trị nhận dạng tôi vừa tạo thông qua một chèn? Tác động của những tuyên bố này về hiệu suất là gì?

  1. SCOPE_IDENTITY()
  2. Chức năng tổng hợp MAX()
  3. CHỌN TOP 1IdentityColumn TỪ Tên bảngORDER BY IdentityColumn DESC

1
Sử dụng postgreSQL và bạn sẽ có nó từ kệ postgresql.org/docs/9.1/static/sql-insert.html
Yevgeniy Afanasyev

Tùy chọn Leftfield - nếu bạn có cột Guid trong bảng và bạn có thể tạo Guid mới và chèn nó vào cột mới trong khi chèn - sau đó bạn có thể chọn hàng có Guid đó để lấy ra nhận dạng int được tạo.
niico

Câu trả lời:


56

Sử dụngSCOPE_IDENTITY() nếu bạn đang chèn một hàng đơn và muốn lấy ID đã được tạo.

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

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

SELECT SCOPE_IDENTITY();

Kết quả:

----
1

Sử dụng OUTPUTmệnh đề nếu bạn đang chèn nhiều hàng và cần truy xuất bộ ID đã được tạo.

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

Kết quả:

----
2
3

và tại sao đây là lựa chọn nhanh nhất tốt nhất?

Bên cạnh hiệu suất, đây là những cái duy nhất được đảm bảo là chính xác ở mức cô lập mặc định và / hoặc với nhiều người dùng. Ngay cả khi bạn bỏ qua khía cạnh chính xác, SQL Server vẫn giữ giá trị được chèn SCOPE_IDENTITY()trong bộ nhớ, do đó, điều này sẽ nhanh hơn so với việc chạy và chạy truy vấn riêng biệt của bạn đối với bảng hoặc đối với các bảng hệ thống.

Bỏ qua khía cạnh chính xác cũng giống như nói với người đưa thư rằng anh ta đã làm tốt việc gửi thư ngày hôm nay - anh ta đã hoàn thành tuyến đường của mình nhanh hơn 10 phút so với thời gian trung bình của mình, vấn đề là, không có thư nào được gửi đến đúng nhà.

Không sử dụng bất kỳ điều nào sau đây:

  • @@IDENTITY - vì điều này không thể được sử dụng trong tất cả các kịch bản, ví dụ: khi một bảng có cột nhận dạng có một trình kích hoạt cũng chèn vào một bảng khác có cột nhận dạng riêng - bạn sẽ nhận lại giá trị sai.
  • IDENT_CURRENT()- Tôi đi vào chi tiết về vấn đề này ở đây , và các ý kiến ​​cũng rất hữu ích khi đọc, nhưng về cơ bản, dưới sự tương tranh, bạn sẽ thường nhận được câu trả lời sai.
  • MAX()hoặc TOP 1- bạn sẽ phải bảo vệ hai câu lệnh bằng cách ly nối tiếp để đảm bảo rằng MAX()bạn nhận được không phải là của người khác. Điều này là đắt hơn nhiều so với chỉ sử dụng SCOPE_IDENTITY().

Các hàm này cũng thất bại bất cứ khi nào bạn chèn hai hoặc nhiều hàng và cần tất cả các giá trị nhận dạng được tạo - tùy chọn duy nhất của bạn là OUTPUTmệnh đề.


Thêm một câu hỏi kích hoạt trong trí nhớ của tôi. Khi nào chúng ta cần nhận được danh tính cuối cùng được tạo trong một bảng cụ thể bởi bất kỳ phiên hoặc người dùng nào, cách duy nhất đúng và tốt nhất là MAX () của cột đó?
AA.SC

Sẽ thế nào khi tôi chèn nhiều hàng vào một bảng, SCOPE_IDENTITY () sẽ luôn trả về danh tính được tạo cuối cùng? Điều gì xảy ra nếu cột là khóa chính nhưng không phải là cột Danh tính?
AA.SC

@ AA.SC có, nó sẽ trả lại cái cuối cùng. Nếu nó không phải là một cột danh tính, thì không, không có chức năng nào trong số này sẽ hoạt động. Giá trị PK đến từ đâu trong trường hợp đó?
Aaron Bertrand

Tôi đã thấy điều này cho một cột trong ứng dụng của chúng tôi trong đó cột thuộc loại INT và các nhà phát triển sử dụng MAX (tên cột) +1 khi họ cần chèn một bản ghi mới
AA.SC

Sau đó, họ đã biết những giá trị họ vừa chèn. SQL Server không có cách nào để nói với bạn điều đó (bạn không thể dựa vào việc kéo MAX một lần nữa sau khi thực tế, trừ khi bạn hoàn toàn cô lập toàn bộ giao dịch của mình, điều này sẽ không tốt cho hiệu suất hoặc đồng thời).
Aaron Bertrand

7

Ngoài hiệu suất, tất cả đều có ý nghĩa khá khác nhau.

SCOPE_IDENTITY()sẽ cung cấp cho bạn giá trị nhận dạng cuối cùng được chèn vào bất kỳ bảng nào trong phạm vi hiện tại (scope = batch, thủ tục được lưu trữ, v.v. nhưng không phải trong phạm vi, giả sử, một kích hoạt đã được kích hoạt bởi phạm vi hiện tại).

IDENT_CURRENT()sẽ cung cấp cho bạn giá trị nhận dạng cuối cùng được chèn vào một bảng cụ thể từ bất kỳ phạm vi nào , bởi bất kỳ người dùng nào .

@@IDENTITYcung cấp cho bạn giá trị nhận dạng cuối cùng được tạo bởi câu lệnh INSERT gần đây nhất cho kết nối hiện tại, bất kể bảng hoặc phạm vi. (Lưu ý bên: Access sử dụng chức năng này và do đó có một số vấn đề với trình kích hoạt chèn giá trị vào các bảng có cột nhận dạng.)

Sử dụng MAX()hoặc TOP 1có thể cung cấp cho bạn kết quả hoàn toàn sai nếu bảng có bước nhận dạng âm hoặc có các hàng được chèn SET IDENTITY_INSERTtrong khi chơi. Đây là một kịch bản thể hiện tất cả những điều này:

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

Tóm tắt: gắn bó với SCOPE_IDENTITY(), IDENT_CURRENT()hoặc @@IDENTITY, và đảm bảo bạn đang sử dụng một thứ trả về những gì bạn thực sự cần.


1
Tại sao bạn khuyến khích việc sử dụng IDENT_CURRENT()@@IDENTITYkhi kịch bản của chính bạn chứng minh rằng họ đưa ra kết quả không chính xác?
Aaron Bertrand

1
@AaronBertrand Tôi không chắc là mình theo dõi. Giá trị nhận dạng cuối cùng được tạo là 8998 (chú ý bước này là -1) và đó là giá trị IDENT_CURRENT()trả về. MAX () không bao giờ trả về giá trị đúng ngoài hàng đầu tiên, vì id đang đếm ngược và với IDENTITY_INSERT, trên 9005 không phải là giá trị nhận dạng được tạo , do đó không được phản ánh bởi IDENT_CURRENT(). Nhưng nó có thể trả về kết quả "không chính xác" nếu bạn thực sự sau những gì SCOPE_IDENTITY()trả về. Chọn công cụ phù hợp cho công việc.
db2

OP dường như nằm sau giá trị nhận dạng mà họ đã chèn - trong trường hợp này 8998 là không chính xác. Các trường hợp cạnh bạn đề cập (ngược increment và IDENTITY_INSERT trên) hơn nữa tranh luận chống lại sử dụng IDENT_CURRENT theo ý kiến của tôi, và @@ IDENTITY nên không bao giờ thực sự được sử dụng vì sự nguy hiểm của trigger (ngay bây giờ hoặc bổ sung sau). Tôi vẫn đang cố gắng để hiểu tại sao IDENT_CURRENT sẽ là người mà OP muốn sử dụng (đặc biệt là dưới thời đồng thời) hoặc tại sao @@ IDENTITY sẽ được sử dụng bởi bất kỳ ai khi có nhiều phương pháp đáng tin cậy hơn.
Aaron Bertrand

@AaronBertrand Không rõ ràng 100% từ câu hỏi rằng kết quả mong muốn là lần chèn cuối cùng từ phạm vi hiện tại (tùy chọn 1 khác với 2 và 3 về vấn đề đó), vì vậy tôi nghĩ rằng sẽ là một ý tưởng tốt để mô tả cả hai và cách họ khác nhau Nhưng tôi đồng ý rằng @@IDENTITYgần như không bao giờ là cách lý tưởng để có được các giá trị nhận dạng được tạo. Điểm chính là MAX()hoặc TOP 1giống như một phiên bản kém tin cậy hơn IDENT_CURRENT(), đó là một chức năng hoàn toàn tốt để sử dụng nếu bạn hiểu những gì nó làm. Có thể hữu ích cho công việc bảo trì hoặc một cái gì đó.
db2
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.