ChatGPT解决这个技术问题 Extra ChatGPT

如何更新 SQL Server 中的标识列?

我有 SQL Server 数据库,我想更改标识列,因为它以大数字 10010 开头,并且与另一个表相关,现在我有 200 条记录,我想在记录增加之前解决这个问题。

更改或重置此列的最佳方法是什么?


C
Community

您不能更新标识列。 SQL Server 不允许更新标识列,这与您可以使用更新语句对其他列执行的操作不同。

尽管有一些替代方案可以实现类似的要求。

何时需要为新记录更新标识列值

使用 DBCC CHECKIDENT 检查表的当前标识值,如果需要,更改标识值。

DBCC CHECKIDENT('tableName', RESEED, NEW_RESEED_VALUE)

当需要为现有记录更新标识列值时

使用 IDENTITY_INSERT 允许将显式值插入到表的标识列中。

SET IDENTITY_INSERT YourTable {ON|OFF}

例子:

-- Set Identity insert on so that value can be inserted into this column
SET IDENTITY_INSERT YourTable ON
GO
-- Insert the record which you want to update with new value in the identity column
INSERT INTO YourTable(IdentityCol, otherCol) VALUES(13,'myValue')
GO
-- Delete the old row of which you have inserted a copy (above) (make sure about FK's)
DELETE FROM YourTable WHERE ID=3
GO
--Now set the idenetity_insert OFF to back to the previous track
SET IDENTITY_INSERT YourTable OFF

DBCC 重置下一条新记录,但我现在想要更改现有记录。
@sachin 这不是更新现有的 IDENTITY 这是手动插入
@PhillGreggan 是的,这是实现这一目标的最佳解决方案。您无法在更新普通列时更新 Identity 列。
这个接受的答案没有回答问题,即如何更新标识列(例如 UPDATE YourTable SET IdentityCol = 13)。 SET IDENTITY_INSERT YourTable ON 只允许 INSERT,不允许 UPDATE。
我认为用“你不能……”来回答“如何……”问题没有用,或者至少不要将其选为最佳答案,因为它不能解决问题。 @Luv 的回复确实解决了问题,即使它不是一个简单的操作或设计上不推荐。
P
Prahalad Gaggar

如果你的问题是正确的,你想做类似的事情

update table
set identity_column_name = some value

让我告诉你,这不是一个简单的过程,也不建议使用它,因为它可能会关联一些 foreign key

但这里有步骤,请拿一张back-up的桌子

步骤 1- 选择表格的设计视图

https://i.stack.imgur.com/WTTtG.png

第 2 步 - 关闭身份列

https://i.stack.imgur.com/IWra4.png

现在您可以使用 update 查询。

现在redo第 1 步和第 2 步并打开标识列

Reference


我有另一个与此表相关的表,我觉得我不能那样做
因此声明 不建议 :)
@AbdusalamElsherif 但是您问如何更改身份列。
@luv 至少你已经回答了被问到的问题
我使用此过程在我的开发数据库中设置 id。如果有一个命令让我无论如何都可以这样做,那就太好了,但这有效。
R
R S P

你需要

set identity_insert YourTable ON

然后删除您的行并以不同的身份重新插入。

完成插入后,不要忘记关闭 identity_insert

set identity_insert YourTable OFF

这比关闭列的身份更容易和更安全!
对于少量记录,这更安全,更容易,但不能正确回答问题。关闭身份虽然更危险,有时甚至是不可能的,但确实允许您更新身份列。
k
kuklei
--before running this make sure Foreign key constraints have been removed that reference the ID. 

--set table to allow identity to be inserted
SET IDENTITY_INSERT yourTable ON;
GO
--insert everything into a temp table
SELECT * 
INTO #tmpYourTable
FROM yourTable

--clear your table
DELETE FROM yourTable
--insert back all the values with the updated ID column
INSERT INTO yourTable (IDCol, OtherCols)
SELECT ID+1 as updatedID --put any other update logic to the ID here
, OtherCols FROM #tmpYourTable
--drop the temp table
DROP TABLE #tmpYourTable
--put identity back to normal
SET IDENTITY_INSERT yourTable OFF;
GO

如果表非常大,这只是一个性能提示,而不是对表执行删除操作,而是截断表 yourTable。它是瞬时的。但是请注意,不要使用 truncate 回头,因为它没有被记录。
@kuklei- truncate 已记录。这是一个常见的误解。截断在页面级别运行并记录它删除的页面列表。这就是为什么它如此之快。 DELETE 是基于行的,如果涉及 FK,则需要,truncate 不会处理它们并显示错误。当然,无论哪种情况,您都需要使用完全恢复模式(不是简单)并进行日志备份。可以恢复到截断操作之前。不管是什么型号,你也可以试一试... BEGIN TRAN TRUNCATE table MyTable ROLLBACK。您的数据仍然存在。
D
Darren

尝试使用 DBCC CHECKIDENT

DBCC CHECKIDENT ('YourTable', RESEED, 1);

这不允许更新标识列。该语句仅重置插入新记录时将使用的标识值,不会更改任何记录。
b
befree2j
SET IDENTITY_INSERT dbo.TableName ON
INSERT INTO dbo.TableName 
(
    TableId, ColumnName1, ColumnName2, ColumnName3
)
VALUES
(
    TableId_Value, ColumnName1_Value, ColumnName2_Value, ColumnName3_Value
)

SET IDENTITY_INSERT dbo.TableName OFF

使用 Identity_Insert 时不要忘记包含列名,因为 sql 不允许您在不指定它们的情况下插入


I
Igor Borisenko
DBCC CHECKIDENT(table_name, RESEED, value)

table_name = 给你要重置的表值

值=初始值为零,以 1 开始标识列


u
user4002899

将您的表复制到没有标识列的新表中。

    select columns into newtable from yourtable

使用新种子将标识列添加到 newtable 并将其作为主键

    ALTER TABLE tableName ADD id MEDIUMINT NOT NULL AUTO_INCREMENT KEY

D
DaveShaw

您还可以使用 SET IDENTITY INSERT 将值插入标识列。

例子:

SET IDENTITY_INSERT dbo.Tool ON
GO

然后您可以将所需的值插入到标识列中。


J
Jekin Kalariya
ALTER TABLE tablename add newcolumn int
update tablename set newcolumn=existingcolumnname
ALTER TABLE tablename DROP COLUMN existingcolumnname;
EXEC sp_RENAME 'tablename.oldcolumn' , 'newcolumnname', 'COLUMN'
update tablename set newcolumnname=value where condition

然而,上面的代码只有在没有主外键关系的情况下才有效


D
Dale K

使用命令生成器的 C# 程序员的完整解决方案

首先,您必须了解以下事实:

在任何情况下,您都无法修改标识列,因此您必须删除该行并重新添加新标识。

您不能从列中删除身份属性(您必须删除到列)

来自 .net 的自定义命令生成器始终会跳过标识列,因此您不能将其用于此目的。

所以,一旦知道了,你要做的就是。要么编写你自己的 SQL Insert 语句,要么编写你自己的插入命令生成器。或者使用我为你编程的这个。给定一个 DataTable,生成 SQL 插入脚本:

public static string BuildInsertSQLText ( DataTable table )
{
    StringBuilder sql = new StringBuilder(1000,5000000);
    StringBuilder values = new StringBuilder ( "VALUES (" );
    bool bFirst = true;
    bool bIdentity = false;
    string identityType = null;

    foreach(DataRow myRow in table.Rows) 
    {
        sql.Append( "\r\nINSERT INTO " + table.TableName + " (" );

        foreach ( DataColumn column in table.Columns )
        {
            if ( column.AutoIncrement )
            {
                bIdentity = true;

                switch ( column.DataType.Name )
                {
                    case "Int16":
                        identityType = "smallint";
                        break;
                    case "SByte":
                        identityType = "tinyint";
                        break;
                    case "Int64":
                        identityType = "bigint";
                        break;
                    case "Decimal":
                        identityType = "decimal";
                        break;
                    default:
                        identityType = "int";
                        break;
                }
            }
            else
            {
                if ( bFirst )
                    bFirst = false;
                else
                {
                    sql.Append ( ", " );
                    values.Append ( ", " );
                }
                sql.Append ("[");
                sql.Append ( column.ColumnName );
                sql.Append ("]");

                //values.Append (myRow[column.ColumnName].ToString() );

                if (myRow[column.ColumnName].ToString() == "True")
                    values.Append("1");
                else if (myRow[column.ColumnName].ToString() == "False")
                    values.Append("0");
                else if(myRow[column.ColumnName] == System.DBNull.Value)    
                    values.Append ("NULL");
                else if(column.DataType.ToString().Equals("System.String"))
                {
                    values.Append("'"+myRow[column.ColumnName].ToString()+"'");
                }
                else
                    values.Append (myRow[column.ColumnName].ToString());
                    //values.Append (column.DataType.ToString() );
            }
        }
        sql.Append ( ") " );
        sql.Append ( values.ToString () );
        sql.Append ( ")" );

        if ( bIdentity )
        {
            sql.Append ( "; SELECT CAST(scope_identity() AS " );
            sql.Append ( identityType );
            sql.Append ( ")" );
        }
        bFirst = true;
        sql.Append(";");
        values = new StringBuilder ( "VALUES (" );
    } //fin foreach
    return sql.ToString ();
}

D
Dharman

我有类似的问题我需要更新一些我所做的ID(我需要将它们增加10k):

set identity_insert YourTable ON

INSERT INTO YourTable 
   ([ID]
   ,[something1]
   ,[something2]
   ,[something3])
SELECT 
   ([ID] + 10000)
   ,[something1]
   ,[something2]
   ,[something3]) 
FROM YourTable
WHERE something1 = 'needs updeted id' 
   AND something2 = 'some other condition'

set identity_insert YourTable OFF

DELETE FROM YourTable 
WHERE ID >= 'your old ID From' 
   AND ID <= 'Your old ID To' 

就是这样。希望您理解这个逻辑,在我的例子中,还有与其他表的 PK-FK 键连接,这意味着我必须更新它们,然后才能从 'YourTable' 原始行中删除。

我知道已经有答案了,我只是想以 SQL 查询为例,


S
Softec

我首先使用 DBCC 然后使用插入解决了这个问题。例如,如果您的表是

首先在表上设置新的当前 ID 值为 NEW_RESEED_VALUE

MyTable { IDCol, colA, colB }

    DBCC CHECKIDENT('MyTable', RESEED, NEW_RESEED_VALUE)

那么你可以使用

    insert into MyTable (colA, ColB) select colA, colB from MyTable

这将复制您的所有记录,但使用以 NEW_RESEED_VALUE 开头的新 IDCol 值。一旦您删除/移动了它们的外键引用(如果有),您就可以删除更高的 ID 值重复行。


这是一个不错的想法,但 New_reseed_value 是当前种子,下一个将使用的数字将比该值多 1。因此,如果您希望下一个插入的行是标识 10,则 NEW_RESEED_VALUE 必须设置为 9。
S
Sean H. Worthington

您可以使用以下代码创建一个新表。

SELECT IDENTITY (int, 1, 1) AS id, column1, column2
INTO dbo.NewTable
FROM dbo.OldTable

然后删除旧数据库,并将新数据库重命名为旧数据库的名称。注意: column1 和 column2 代表旧表中要保留在新表中的所有列。


m
mortenma71

我做了以下事情:

MOVE 相关数据到临时存储 UPDATE 主键/标识列值(删除和创建约束) RE-INSERT 具有新外键值的相关数据

我将我的解决方案包装在一个存储过程中:

CREATE PROCEDURE [dbo].[UpdateCustomerLocationId]
    @oldCustomerLocationId INT,
    @newCustomerLocationId INT
AS
  /* 
    Updates CustomerLocation.CustomerLocationId @oldCustomerLocationId to @newCustomerLocationId
    
    Example:
        EXEC [dbo].[UpdateCustomerLocationId] 
            @oldCustomerLocationId = 6154874, 
            @newCustomerLocationId = 50334;
    */

BEGIN

    SET NOCOUNT ON;
    
    -- exit if @oldCustomerLocationId does not exists
    IF NOT EXISTS (SELECT * FROM dbo.CustomerLocation cl WHERE cl.CustomerLocationId = @oldCustomerLocationId)
    BEGIN
        PRINT CONCAT('CustomerLocationId ''', @oldCustomerLocationId, ''' (@oldCustomerLocationId) does not exist in dbo.CustomerLocation');
        RETURN 1; -- 0 = success, > 0 = failure
    END

    -- exit if @newCustomerLocationId already exists
    IF EXISTS (SELECT * FROM dbo.CustomerLocation cl WHERE cl.CustomerLocationId = @newCustomerLocationId)
    BEGIN
        PRINT CONCAT('CustomerLocationId ''', @newCustomerLocationId, ''' (@newCustomerLocationId) already exists in dbo.CustomerLocation');
        RETURN 2; -- 0 = success, > 0 = failure
    END
    

    BEGIN TRAN;


        BEGIN -- MOVE related data into temporary storage

            IF EXISTS (SELECT * FROM dbo.CustomerLocationData t WHERE t.CustomerLocationId = @oldCustomerLocationId) BEGIN
    
                IF OBJECT_ID('tempdb..#CustomerLocationData') IS NOT NULL
                    DROP TABLE #CustomerLocationData;

                SELECT * INTO #CustomerLocationData FROM dbo.CustomerLocationData t WHERE t.CustomerLocationId = @oldCustomerLocationId;
                DELETE t FROM dbo.CustomerLocationData t WHERE t.CustomerLocationId = @oldCustomerLocationId;

            END

        END


        BEGIN -- UPDATE dbo.CustomerLocation

            -- DROP CONSTRAINTs
            ALTER TABLE [dbo].[CustomerLocation] DROP CONSTRAINT [UC_CustomerLocation];

            -- INSERT OLD record with new CustomerLocationId
            SET IDENTITY_INSERT dbo.CustomerLocation ON;

            INSERT INTO dbo.CustomerLocation
            (
                CustomerLocationId, CustomerId, LocationId, CustomerLocationIdent, CustomerLocationIdent2, LocationIdent, LocationName, CustomerDistrictId, 
                CustomerLocationGUID, UpdatedOn, IssueManager, EnrollSelfMonitoring, TemperatureControlDeadlineHour, CreatedOn, OperationBegin, ActiveCustomer, 
                Comments, LocationName2, ParentGroup, TempString1, TempString2, TempString3, TempString4, TempString5, AutoInheritFromLocation, ClassificationPrimary
            )
            SELECT @newCustomerLocationId AS CustomerLocationId, CustomerId,LocationId, CustomerLocationIdent, CustomerLocationIdent2, LocationIdent, LocationName, CustomerDistrictId,
                CustomerLocationGUID, UpdatedOn, IssueManager, EnrollSelfMonitoring, TemperatureControlDeadlineHour, CreatedOn, OperationBegin, ActiveCustomer,
                Comments,LocationName2, ParentGroup, TempString1, TempString2, TempString3, TempString4, TempString5, AutoInheritFromLocation, ClassificationPrimary
            FROM dbo.CustomerLocation 
            WHERE CustomerLocationId = @oldCustomerLocationId;

            SET IDENTITY_INSERT dbo.CustomerLocation OFF;

            -- DELETE OLD record
            DELETE cl FROM dbo.CustomerLocation cl WHERE cl.CustomerLocationId = @oldCustomerLocationId;

            -- ADD CONSTRAINTS
            ALTER TABLE [dbo].[CustomerLocation] ADD CONSTRAINT [UC_CustomerLocation] UNIQUE NONCLUSTERED ([CustomerId], [LocationId]);

        END


        BEGIN -- re-INSERT related data from temporary storage

            IF OBJECT_ID('tempdb..#CustomerLocationData') IS NOT NULL BEGIN
                SET IDENTITY_INSERT dbo.CustomerLocationData ON;
                INSERT INTO dbo.CustomerLocationData (Guid, CustomerLocationId, CustomerLocationDataTypeId, Date, Category, Data)
                SELECT Guid, @newCustomerLocationId CustomerLocationId, CustomerLocationDataTypeId, Date, Category, Data FROM #CustomerLocationData;
                SET IDENTITY_INSERT dbo.CustomerLocationData OFF;
            END

        END


    COMMIT TRAN;

END

u
user3407335

这发生在我身上,因为我做了一个合并,更新了我正在合并的 ID。

无效的示例(注意 ClownID):

    MERGE ArchitectMain.dbo.BackendClowns AS TGT 
   USING (
    SELECT ClownID
        ,ClownName
        ,Description
        ,Active
        ,EmailSubject
        ,AddedBy
        ,Added
    FROM #temptable1
    ) AS SRC(ClownID, ClownName, Description, Active, EmailSubject, AddedBy, Added)
    ON (TGT.ClownID = SRC.ClownID)
WHEN MATCHED
    THEN
        UPDATE
        SET ClownID = SRC.ClownID
            ,ClownName = SRC.ClownName
            ,Description = SRC.Description
            ,Active = SRC.Active
            ,EmailSubject = SRC.EmailSubject
            ,AddedBy = SRC.AddedBy
            ,Added = SRC.Added;

有效的示例(注意 ClownID):

MERGE ArchitectMain.dbo.BackendClowns AS TGT
USING (
    SELECT ClownID
        ,ClownName
        ,Description
        ,Active
        ,EmailSubject
        ,AddedBy
        ,Added
    FROM #temptable1
    ) AS SRC(ClownID, ClownName, Description, Active, EmailSubject, AddedBy, Added)
    ON (TGT.ClownID = SRC.ClownID)
WHEN MATCHED
    THEN
        UPDATE
        SET ClownName = SRC.ClownName
            ,Description = SRC.Description
            ,Active = SRC.Active
            ,EmailSubject = SRC.EmailSubject
            ,AddedBy = SRC.AddedBy
            ,Added = SRC.Added;
            

J
Jose Manuel Lepe

正如我所见,有几种方法可以做到这一点。但我认为最好和更快的方法是以下一种:

身份列有一个计数器,不一定与注册的列相同,您可以使用以下 SQL 命令查看此计数器的值:

DBCC CHECKIDENT('tableName', NORESEED);

然后,如果您想编辑身份列,您将无法进行,但我建议您在将计数器重新设置为您需要的号码后进行新的注册。要为计数器重新设定种子,请使用以下命令:

DBCC CHECKIDENT('tableName', RESEED, desiredNumber);

K
Ken Forslund

如果您特别需要将主键值更改为不同的数字(例如 123 -> 1123)。身份属性阻止更改 PK 值。设置 Identity_insert 不起作用。如果您有级联删除,则不建议执行插入/删除(除非您关闭参照完整性检查)。

编辑:较新版本的 SQL 不允许更改 syscolumns 实体,因此我的部分解决方案必须以艰难的方式完成。请参阅此 SO,了解如何从主键中删除身份:Remove Identity from a column in a table 此脚本将关闭 PK 上的身份:

***********************

sp_configure 'allow update', 1
go
reconfigure with override
go


update syscolumns set colstat = 0 --turn off bit 1 which indicates identity column
where id = object_id('table_name') and name = 'column_name'
go


exec sp_configure 'allow update', 0
go
reconfigure with override
go

***********************

接下来,您可以设置关系,以便它们更新外键引用。否则,您需要关闭关系执行。此 SO 链接显示如何:How can foreign key constraints be temporarily disabled using T-SQL?

现在,您可以进行更新了。我编写了一个简短的脚本来编写基于相同列名的所有更新 SQL(在我的情况下,我需要将 CaseID 增加 1,000,000:

select 
'update ['+c.table_name+'] SET ['+Column_Name+']=['+Column_Name+']+1000000'
from Information_Schema.Columns as c
JOIN Information_Schema.Tables as t ON t.table_Name=c.table_name and t.Table_Schema=c.table_schema and t.table_type='BASE TABLE'
where Column_Name like 'CaseID' order by Ordinal_position

最后,重新启用参照完整性,然后重新启用主键上的标识列。

注意:我看到一些人在这些问题上问为什么。就我而言,我必须将来自第二个生产实例的数据合并到一个主数据库中,这样我才能关闭第二个实例。我只需要操作数据的所有 PK/FK 不会发生冲突。元数据 FK 是相同的。


当我这样做时它起作用了,这就是为什么我把它全部写下来的原因
从未支持直接更新 syscolumns,最后一次在 SQL Server 2000 上工作。在 SQL Server 2005 中,syscolumns 被替换为视图,现在您将收到错误:Ad hoc updates to system catalogs are not allowed. 您可以使用 sp_helptext 'sys.syscolumns' 查看视图文本
在 SQL2005 上工作。现在版本问题很重要,但下次以该信息为先。要在较新版本上执行我建议的操作,可能需要克隆标识列并删除原始列,进行值更新,然后将替换列提升回标识。
刚刚在 SQL 2005 SP2 (9.00.3042.00) 上进行了测试,它以 Ad hoc updates to system catalogs are not allowed. 失败