如何在 SQL Server 中重新设置标识列?

Posted

技术标签:

【中文标题】如何在 SQL Server 中重新设置标识列?【英文标题】:How to reseed the identity column in SQL Server? 【发布时间】:2014-05-02 17:47:02 【问题描述】:

我在 SQL Server 中有一个名为 Personal_Info 的表。此表有一个名为Vol_ID 的列,(Is Identity) 属性为Yes

现在我插入了大约 175568 行作为测试。然后我删除了大约 568 行,但是当我再次插入数据时,列 Vol_ID 从 175569 开始而不是从 175001 开始。

我想插入数据并让Vol_ID 列从其中的最后一个数字开始。例如,如果最后一行是 15,则下一行应该从 16 开始,即使我已经插入但我删除了它。

我需要一个存储过程或触发器,它可以强制插入操作填充缺失的 id,检查最后存在的行,如果在存在之后有缺失的 id,然后填充它。

我有这个程序来进行插入



      USE [VolunteersAffairs] 

    go 

    /****** Object:  StoredProcedure [dbo].[AddVolunteer]    Script Date: 05/02/2014      21:12:36 ******/ 
    SET ansi_nulls ON 

    go 

    SET quoted_identifier ON 

    go 

    ALTER PROCEDURE [dbo].[Addvolunteer] @Vol_Name                 NVARCHAR(255), 
                                         @Vol_Zone                 NVARCHAR(255), 
                                         @vol_street               NVARCHAR(255), 
                                         @Vol_Sex                  INT, 
                                         @Vol_Date_of_Birth        DATE, 
                                         @Vol_Home_Phone           INT, 
                                         @Vol_Work_Phone           INT, 
                                         @Vol_Mobile1              INT, 
                                         @Vol_Mobile2              INT, 
                                         @Vol_Email                NVARCHAR(255), 
                                         @Vol_Job                  NVARCHAR(255), 
                                         @Vol_Affiliation          NVARCHAR(255), 
                                         @vol_Education            INT, 
                                         @vol_Education_Place      NVARCHAR(255), 
                                         @vol_Education_Department NVARCHAR(255), 
                                         @vol_Interesting          INT, 
                                         @vol_Notes                NVARCHAR(255), 
                                         @Team_ID                  INT 
    AS 
        INSERT INTO personal_info 
                    (vol_name, 
                     vol_zone, 
                     vol_street, 
                     vol_sex, 
                     vol_date_of_birth, 
                     vol_home_phone, 
                     vol_work_phone, 
                     vol_mobile1, 
                     vol_mobile2, 
                     vol_email, 
                     vol_job, 
                     vol_affiliation, 
                     vol_education, 
                     vol_education_place, 
                     vol_education_department, 
                     vol_interesting, 
                     team_id, 
                     vol_notes) 
        VALUES      (@Vol_Name, 
                     @Vol_Zone, 
                     @vol_street, 
                     @Vol_Sex, 
                     @Vol_Date_of_Birth, 
                     @Vol_Home_Phone, 
                     @Vol_Work_Phone, 
                     @Vol_Mobile1, 
                     @Vol_Mobile2, 
                     @Vol_Email, 
                     @Vol_Job, 
                     @Vol_Affiliation, 
                     @vol_Education, 
                     @vol_Education_Place, 
                     @vol_Education_Department, 
                     @vol_Interesting, 
                     @Team_ID, 
                     @vol_Notes) 

请帮我写下丢失的身份证

【问题讨论】:

您需要重新设置身份列。 一句忠告:不要这样做!你应该永远不要 “回收”以前用于新行的标识值。不要这样做 - 如果你这样做,你最终会陷入数据库地狱! Stop making assumptions about IDENTITY,别再担心差距了 - 它们真的不是问题! 【参考方案1】:

您需要像下面这样重新设置身份列

DBCC CHECKIDENT(Personal_Info, RESEED, 175001)

引用自 MSDN

权限:

调用者必须拥有该表,或者是 sysadmin 固定服务器的成员 角色、db_owner 固定数据库角色或 db_ddladmin 固定 数据库角色。

所以。它是表的所有者(或)DBA 可以执行标识列的reseed 而不是普通用户。此外,您为什么还要允许您的应用程序用户重新设定身份列的值?这根本没有意义。

编辑:

在这种情况下,请查看下面的@Brennan 答案。简而言之,您应该从存储过程执行插入。我的意思是你的APP应该调用SP并执行插入。如下所示(示例代码)。此外,在这种情况下,删除 vol_id 列的标识属性(vol_id 应该只是 vol_id int not null unique

create procedure sp_insert-person
@name varchar(10)
as 
begin
declare @last_id int;
select @last_id = top 1 Vol_ID from Personal_Info order by Vol_ID desc;
insert into Personal_Info(Vol_ID,name) 
values(@last_id+1,@name);
end

您的应用程序将调用类似的过程

exec sp_insert-person @name = 'user2251369'

最终编辑:

请参阅此帖子 RESEED identity columns on the database,了解为什么应避免使用 reseeding IDENTITY 值(marc_s 给出的一个很好的小解释)。

您的最终程序应如下所示

USE [VolunteersAffairs]
GO
/****** Object:  StoredProcedure [dbo].[AddVolunteer]    
 Script Date: 05/02/2014      21:12:36 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[AddVolunteer]
@Vol_Name nvarchar(255), 
@Vol_Zone nvarchar(255), 
@vol_street nvarchar(255), 
@Vol_Sex int, 
@Vol_Date_of_Birth date, 
@Vol_Home_Phone int, 
@Vol_Work_Phone int, 
@Vol_Mobile1 int,    
@Vol_Mobile2 int, 
@Vol_Email nvarchar(255), 
@Vol_Job nvarchar(255), 
@Vol_Affiliation nvarchar(255), 
@vol_Education int, 
@vol_Education_Place nvarchar(255), 
@vol_Education_Department nvarchar(255), 
@vol_Interesting int, 
@vol_Notes nvarchar(255), 
@Team_ID int
As
BEGIN
DECLARE @last_vol_id int;
select top 1 @last_vol_id = Vol_ID from Personal_Info order by Vol_ID desc;

insert into Personal_Info
(Vol_Id,
Vol_Name, 
Vol_Zone, 
vol_street, 
Vol_Sex, 
Vol_Date_of_Birth, 
Vol_Home_Phone, 
Vol_Work_Phone, 
Vol_Mobile1, 
Vol_Mobile2, 
Vol_Email, 
Vol_Job, 
Vol_Affiliation, 
vol_Education, 
vol_Education_Place, 
vol_Education_Department, 
vol_Interesting, 
Team_ID, 
vol_Notes) 
values 
(@last_vol_id+1,
@Vol_Name, 
@Vol_Zone, 
@vol_street, 
@Vol_Sex, 
@Vol_Date_of_Birth, 
@Vol_Home_Phone, 
@Vol_Work_Phone, 
@Vol_Mobile1, 
@Vol_Mobile2, 
@Vol_Email, 
@Vol_Job, 
@Vol_Affiliation, 
@vol_Education, 
@vol_Education_Place, 
@vol_Education_Department, 
@vol_Interesting, 
@Team_ID, 
@vol_Notes);
END

【讨论】:

这对我有用,不适用于应该使用该应用程序的普通用户,如果他/她插入一些行然后删除它,那么当他们插入新数据时,应该检查最后一行然后重新播种。谢谢 @user2251369,请参阅编辑后的答案。您根本不应该允许您的应用用户执行此任务。 谁让我建立这个应用程序告诉我序列号是最重要的,它应该自动插入。 @user2251369,在这种情况下,请查看下面的 Brennan 答案。简而言之,您应该从存储过程执行插入。我的意思是你的APP应该调用SP并执行插入。【参考方案2】:

您尝试做的不是标识列的用途 - 不能保证不会有间隙,删除记录不会重新使用丢失的标识。

如果您确实需要一个连续的、自动递增的 id 字段,并且没有间隙,那么您最好使用存储过程进行插入(您需要添加逻辑以重用丢失的 id) , 或者定义一个触发器来强制执行您想要的操作 - 但尝试强制标识列执行您正在尝试执行的操作是错误的方法。

【讨论】:

谢谢,但如果可以编写一个存储过程或触发器的示例,将强制应用程序写入丢失的 id,这将是有帮助的,再次感谢【参考方案3】:

正如其他人所说,您可以重新设定身份。但是,这仅在您最后删除时才有效。填补身份的空白将非常具有挑战性,您最终将不得不关闭身份。

如果这是种子数据的小案例,您可以打开身份插入。 enter link description here

SET IDENTITY_INSERT your table ON

Here is another question highlighting the other concerns

【讨论】:

这绝不是一个好主意。如果你这样做..那么最好不要有标识列。 设置 Identity_Insert On 永远不是一个好主意?任何绝对都很难捍卫。当我需要在开发过程中播种表格并清除某些内容时,我经常使用它。它可供我们使用,但我同意这种情况很少见。 not good idea;我的意思是,不要强制写入身份,而是重新设置身份值或其他方式。 SET IDENTITY_INSERT 默认为OFF。这背后肯定有一些很好的理由。 哦,我明白了,我同意。我想说的是重新植入身份并不能解决全部问题。 如果用户有可能从列表中间删除项目,然后重新设置身份并不能解决这些差距。【参考方案4】:

这就是魔法。在插入之前执行此操作。

declare @i int
select @i=max(id) from Personal_Info
DBCC CHECKIDENT (Personal_Info, RESEED, @i)

【讨论】:

以上是关于如何在 SQL Server 中重新设置标识列?的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法确保 SQL Server 标识列值即使在多次服务器重新启动后也始终保持一致?

如何在 SQL Server 中添加标识列?

在 SQL Server 2017 中使用标识列间隙

如何在不删除sql server表中数据的情况下,让自增的id列从1开始

如何在不删除sql server表中数据的情况下,让自增的id列从1开始

在 SQL Server 2005 中将列标识上的标识设置为另一个表的列