Sql server SQL Server中唯一ID的批量修改
[这是一个有点不寻常的问题,我知道…] 我需要的是一个脚本,它可以将数据库中的每个唯一id值更改为新的id值。问题是,我们的配置表可以在我们的软件实例之间导出,这些实例对现有id敏感。几年前,我们在开发标准配置和客户实例之间设置了足够大的id差距,但现在差距不够大:-例如,当客户导入我们的标准配置时,我们会遇到id冲突 执行以下操作的sql脚本绝对是我们可以做的最简单/最短时间段的事情。例如,固定代码太复杂,容易出错。请注意,我们并没有在这里消除这个问题。仅仅将差距从1000改变为1000000或更多现有差距需要5年才能填补 我认为最简单的解决办法是: 将我们所有的表都更改为UPDATE\u CASCADE,它们中没有一个是-这将大大简化脚本 使用我们想要的新的最低id创建标识表 对于每个表,在必要时使用identity insert修改器标志将id修改为identity表中的下一个id。也许在处理每个表之后,我们可以重置标识表。 关闭UPDATE_CASCADE,并删除标识表。Sql server SQL Server中唯一ID的批量修改,sql-server,database,scripting,identity,Sql Server,Database,Scripting,Identity,[这是一个有点不寻常的问题,我知道…] 我需要的是一个脚本,它可以将数据库中的每个唯一id值更改为新的id值。问题是,我们的配置表可以在我们的软件实例之间导出,这些实例对现有id敏感。几年前,我们在开发标准配置和客户实例之间设置了足够大的id差距,但现在差距不够大:-例如,当客户导入我们的标准配置时,我们会遇到id冲突 执行以下操作的sql脚本绝对是我们可以做的最简单/最短时间段的事情。例如,固定代码太复杂,容易出错。请注意,我们并没有在这里消除这个问题。仅仅将差距从1000改变为1000000
有人有这个或部分脚本的脚本吗?。我将投票表决任何有用的东西:。为什么不在标准配置值中使用负数,而在其他方面继续使用正数?不幸的是,Sql Server世界中不存在更新级联。我建议对每个表重新设置密钥,执行以下伪代码
BACKUP DATABASE
CHECK BACKUP WORKS!
FOR EACH TABLE TO BE RE-KEYED
DROP ALL FOREIGN KEY CONSTRAINTS, INDEXES ETC FROM TABLE
SELECT ID + Number, ALL_OTHER_FIELDS INTO TEMP_TABLE FROM TABLE
RENAME TABLE OLD_TABLE
RENAME TEMP_TABLE TABLE
FOR ALL TABLES REFERENCING THIS TABLE
UPDATE FOREIGN_KEY_TABLE SET FK_ID = FK_ID + new number
END FOR
RE-APPLY FOREIGN KEY CONSTRAINTS, INDEXES ETC FROM TABLE
END FOR
检查它是否仍然有效
这个过程可以通过DMO/SMO对象实现自动化,但根据涉及的表的数量,我认为使用ManagementStudio生成可以编辑的脚本可能会更快。毕竟,您只需要每五年执行一次。这里我们将介绍SQL 2005的代码。它是巨大的,它是黑客,但它将工作,除非你有一个主键,是一个复合的其他两个主键的情况下 如果有人可以用MrTelly更快的id添加重新编写它,而不需要为每个更新的行从游标构建sql,那么我将把它标记为可接受的答案。如果我没有注意到新的答案,请向上投票-然后我会注意到:
BEGIN TRAN
SET NOCOUNT ON;
DECLARE @newLowId INT
SET @newLowId = 1000000
DECLARE @sql VARCHAR(4000)
--**** SELECT ALL TABLES WITH IDENTITY COLUMNS ****
DECLARE tables SCROLL CURSOR
FOR
SELECT '[' + SCHEMA_NAME(schema_id) + '].[' + t.name + ']', c.name
FROM sys.identity_columns c
INNER JOIN sys.objects t
on c.object_id = t.object_id
WHERE t.type_Desc = 'USER_TABLE'
OPEN tables
DECLARE @Table VARCHAR(100)
DECLARE @IdColumn VARCHAR(100)
CREATE Table #IdTable(
id INT IDENTITY(1,1),
s CHAR(1)
)
FETCH FIRST FROM tables
INTO @Table, @IdColumn
WHILE @@FETCH_STATUS = 0
BEGIN
PRINT('
****************** '+@Table+' ******************
')
--Reset the idtable to the 'low' id mark - remove this line if you want all records to have distinct ids across the database
DELETE FROM #IdTable
DBCC CHECKIDENT('#IdTable', RESEED, @newLowId)
--**** GENERATE COLUMN SQL (for inserts and deletes - updating identities is not allowed) ****
DECLARE tableColumns CURSOR FOR
SELECT column_name FROM information_schema.columns
WHERE '[' + table_schema + '].[' + table_name + ']' = @Table
AND column_name <> @IdColumn
OPEN tableColumns
DECLARE @columnName VARCHAR(100)
DECLARE @columns VARCHAR(4000)
SET @columns = ''
FETCH NEXT FROM tableColumns INTO @columnName
WHILE @@FETCH_STATUS = 0
BEGIN
SET @columns = @columns + @columnName
FETCH NEXT FROM tableColumns INTO @columnName
IF @@FETCH_STATUS = 0 SET @columns = @columns + ', '
END
CLOSE tableColumns
DEALLOCATE tableColumns
--**** GENERATE FOREIGN ROW UPDATE SQL ****
DECLARE foreignkeys SCROLL CURSOR
FOR
SELECT con.name,
'[' + SCHEMA_NAME(f.schema_id) + '].[' + f.name + ']' fTable, fc.column_name ,
'[' + SCHEMA_NAME(p.schema_id) + '].[' + p.name + ']' pTable, pc.column_name
FROM sys.foreign_keys con
INNER JOIN sysforeignkeys syscon
ON con.object_id = syscon.constid
INNER JOIN sys.objects f
ON con.parent_object_id = f.object_id
INNER JOIN information_schema.columns fc
ON fc.table_schema = SCHEMA_NAME(f.schema_id)
AND fc.table_name = f.name
AND fc.ordinal_position = syscon.fkey
INNER JOIN sys.objects p
ON con.referenced_object_id = p.object_id
INNER JOIN information_schema.columns pc
ON pc.table_schema = SCHEMA_NAME(p.schema_id)
AND pc.table_name = p.name
AND pc.ordinal_position = syscon.rkey
WHERE '[' + SCHEMA_NAME(p.schema_id) + '].[' + p.name + ']' = @Table
OPEN foreignkeys
DECLARE @FKeyName VARCHAR(100)
DECLARE @FTable VARCHAR(100)
DECLARE @FColumn VARCHAR(100)
DECLARE @PTable VARCHAR(100)
DECLARE @PColumn VARCHAR(100)
--**** RE-WRITE ALL IDS IN THE TABLE ****
SET @sql='DECLARE tablerows CURSOR FOR
SELECT CAST('+@IdColumn+' AS VARCHAR) FROM '+@Table+' ORDER BY '+@IdColumn
PRINT(@sql)
exec(@sql)
OPEN tablerows
DECLARE @rowid VARCHAR(100)
DECLARE @id VARCHAR(100)
FETCH NEXT FROM tablerows INTO @rowid
WHILE @@FETCH_STATUS = 0
BEGIN
--generate new id
INSERT INTO #IdTable VALUES ('')
SELECT @id = CAST(@@IDENTITY AS VARCHAR)
IF @rowId <> @Id
BEGIN
PRINT('Modifying '+@Table+': changing '+@rowId+' to '+@id)
SET @sql='SET IDENTITY_INSERT ' + @Table + ' ON
INSERT INTO '+@Table+' ('+@IdColumn+','+@columns+') SELECT '+@id+','+@columns+' FROM '+@Table+' WHERE '+@IdColumn+'='+@rowId
--Updating all foreign rows...
FETCH FIRST FROM foreignkeys
INTO @FKeyName, @FTable, @FColumn, @PTable, @PColumn
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = @sql + '
UPDATE '+@FTable+' SET '+@FColumn+'='+@id+' WHERE '+@FColumn+' ='+@rowId
FETCH NEXT FROM foreignkeys
INTO @FKeyName, @FTable, @FColumn, @PTable, @PColumn
END
SET @sql=@sql + '
DELETE FROM '+@Table+' WHERE '+@IdColumn+'='+@rowId
PRINT(@sql)
exec(@sql)
END
FETCH NEXT FROM tablerows INTO @rowid
END
CLOSE tablerows
DEALLOCATE tablerows
CLOSE foreignkeys
DEALLOCATE foreignkeys
--Revert to normal identity operation - update the identity to the latest id...
DBCC CHECKIDENT(@Table, RESEED, @@IDENTITY)
SET @sql='SET IDENTITY_INSERT ' + @Table + ' OFF'
PRINT(@sql)
exec(@sql)
FETCH NEXT FROM tables
INTO @Table, @IdColumn
END
CLOSE tables
DEALLOCATE tables
DROP TABLE #IdTable
--COMMIT
--ROLLBACK
可以,但这将涉及修改代码,而不是使用标识。保存客户端和配置的代码是相同的,而当前数据存在问题。不管我们做什么,我们都需要修改数据库,所以我们不妨走这条路。这真是太棒了——只需偏移id。我的解决方案会在所有行中移动光标并更新它们。非常烦人和缓慢,但它只有几千行…但请注意,它不会工作,因为IDENTITY_INSERT只适用于插入,而不适用于更新。更新包括一个添加和一个删除。将代码更改为使用SELECT INTO而不是UPDATE-我必须自己做这件事,但我忘记了这一点。