Điều gì có thể khiến ID sai được chèn vào?


7

Tôi có máy chủ SQL Server 2008 (bản dựng 10.0.5500). Đầu tuần này tôi đã chạy cái này trên một cái bàn đã có dữ liệu trong đó :

delete from dbo.table
go    
dbcc checkident('dbo.table',reseed,0)

Khi người dùng đã tạo một bản ghi mới sau này, bằng cách nào đó, ID 0 được chèn vào cột ID, thay vì 1 SQL Server thường đặt nếu nhận dạng (1,1) được định cấu hình cho ID.

Điều này gây ra một số vấn đề kỳ lạ, nhưng việc xóa dữ liệu và chạy rese dẫn đến kết quả là 1 được chèn, như mong đợi. Tôi không thể sao chép vấn đề.

Để tham khảo, đây là định dạng chung cho lưu sp của chúng tôi:

alter procedure dbo._TableSave
    @pk_id int,
    @field varchar(50)
as
    if (@pk_id is null)
    begin
        set nocount on;

        insert into dbo.Table
        (
            Field
        )
        values
        (
            @field
        );
        select scope_identity();
    end
    else
    begin
        update dbo.Table
        set Field=@field
        where PK_ID=@pk_id

        select @pk_id
    end

Có ai biết điều gì có thể khiến SQL Server chèn 0 vào ID khi đáng lẽ nó phải là 1 không?

Chúng tôi không chèn dữ liệu vào các cột định danh (có nhận dạng bật_insert ) ở bất kỳ đâu trong ứng dụng.

Câu trả lời:


10

Việc làm dbcc checkident('dbo.table',reseed,0)sẽ khiến mục tiếp theo trong bảng mới được tạo / cắt ngắn có 0 làm danh tính.

        CREATE TABLE TestIdent
        (
            ID INT NOT NULL CONSTRAINT PK_TestIdent PRIMARY KEY CLUSTERED IDENTITY(1,1)
            , SomeText nvarchar(255)
        );

        dbcc checkident('dbo.TestIdent',reseed,0)

        INSERT INTO TestIdent VALUES ('Test');

        SELECT * FROM dbo.TestIdent;

nhập mô tả hình ảnh ở đây

Điều này khiến danh tính tiếp theo được nhập là 10:

        TRUNCATE TABLE dbo.TestIdent;

        DBCC CHECKIDENT('dbo.TestIdent',reseed,10)

        INSERT INTO TestIdent VALUES ('Test');

        SELECT * FROM dbo.TestIdent;

nhập mô tả hình ảnh ở đây


4
Chỉ khi bảng mới được tạo hoặc đã bị cắt ngắn. Nếu bạn chèn một hàng và xóa nó, bạn sẽ nhận được một kết quả khácCREATE TABLE dbo.[table] (id int identity(1,1));INSERT INTO dbo.[table] DEFAULT VALUES;DELETE FROM dbo.[table];dbcc checkident('dbo.table',reseed,0);INSERT INTO dbo.[table] OUTPUT inserted.* DEFAULT VALUES;DROP TABLE dbo.[table]
Martin Smith

Tôi đồng tình, Martin. Tôi tin rằng cách duy nhất để có được 0 trong trường nhận dạng là bắt đầu với một bảng trống / cắt ngắn.
Max Vernon

@MartinSmith xóa hồ sơ và sắp xếp lại là mô hình mà tôi đã theo dõi. Truncate không hoạt động trên hầu hết các bảng của chúng tôi vì chúng có mối quan hệ FK.
DForck42

9

Tôi đề nghị mã sau đây để ngăn chặn nó xảy ra:
(Thay vì mã bạn đã đăng trong câu hỏi của bạn)

DELETE FROM dbo.table
GO
IF EXISTS 
(
    SELECT * 
    FROM sys.identity_columns 
    WHERE object_id = OBJECT_ID('dbo.table') 
        AND last_value IS NOT NULL
)
DBCC CHECKIDENT ('dbo.table', RESEED, 0);

Nó sẽ ngăn chặn "sắp xếp lại thành 0".

Giải thích:
(Tôi không tìm thấy tài liệu chính thức nào để hỗ trợ nó ...)
Vấn đề là với bảng sys.syscolparstrong một cột có tên idtval
đó là cột chứa TẤT CẢ thông tin cột định danh.
vì bảng đó có một hàng cho mỗi cột trong cơ sở dữ liệu (không chỉ các cột định danh),
một NULLgiá trị trong trường đó sẽ đại diện cho một cột không nhận dạng
(không giống như sys.identity_columnssử dụng NULLcho bảng \ cắt ngắn).

sys.syscolparssử dụng một phương pháp khác để giữ thông tin:
nó sử dụng các 0x01000000010000000100000001định dạng (ví dụ về bản sắc (1,1) mới được tạo ra),
với bên trái 8 chữ số (vị trí 1-8 sau 0x) cho Last_Value,
8 chữ số tiếp theo (vị trí 9-16 sau 0x) cho increment_value,
8 chữ số tiếp theo (vị trí 17-24 sau 0x) cho seed_value,
chữ số thứ 2 từ bên phải là một bí ẩn đối với tôi
và chữ số 1 từ bên phải - cho chúng tôi biết nếu đó là bảng mới (= 1 ) hay không!

trở lại vấn đề của chúng tôi -
khi DBCC CHECKIDENTđược chạy, nó sẽ kiểm tra sys.identity_columns.last_value
xem có phải NULLsau đó nó cập nhật giá trị mới sys.syscolpars.idtvalvới 1ở đúng vị trí không.
sys.identity_columns.last_valuevẫn còn NULL.
khiến giá trị nhận dạng chèn tiếp theo được xử lý như trong bảng mới.
(Tôi giả sử sys.identity_columnslà độc lập sys.syscolparsvì cái đầu tiên được cập nhật ngay lập tức và cái sau chỉ được cập nhật sau khi điểm kiểm tra được thực hiện).

Vì không có cách truy vấn sys.syscolparstrong thời gian thực (nó yêu cầu DAC), nên
đề xuất của tôi là điều kiện ở trên, có thể có thêm ELSEmệnh đề.

một lần nữa, tôi không thể đảm bảo rằng bất kỳ lời giải thích này là đúng, nhưng nó hoạt động :)

Nếu bất cứ ai có thông tin chính xác hơn về DBCC CHECKIDENThoạt động nền,
tôi rất muốn tìm hiểu, xin vui lòng chia sẻ!

Chúc may mắn,
tiếng Tây Ban Nha


0
dbcc checkident('dbo.table',reseed,0)

lệnh này khiến giá trị nhận dạng mới được đặt thành 0. Tôi đã gặp phải vấn đề tương tự.

sau đó tôi áp dụng danh tính reseed cho 1 như thế này

  dbcc checkident('dbo.table',reseed,1)

có, cái đầu tiên đặt id thành 0, vì vậy khi bản ghi tiếp theo được chèn, máy chủ sql sẽ lấy id hiện tại, thêm 1 và nhận 1. id tiếp theo tôi sẽ nhận được là 2.
DForck42
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.