第一
你可能并不需要所有三列:old_id
,external_id
,new_id
。即使将插入,该new_id
列IDENTITY
也将为每一行生成一个新值external_id
。但是,在old_id
和之间external_id
,它们之间几乎是互斥的:或者已经存在一个old_id
值,或者在当前概念中该列仅在NULL
使用external_id
或时才存在new_id
。由于您不会在已存在的行(即具有old_id
值的行)中添加新的“外部” ID ,并且不会有任何新值输入old_id
,因此可以使用一列出于两个目的。
因此,摆脱external_id
列,并重命名old_id
为类似old_or_external_id
或类似的东西。这不需要对任何内容进行任何实际更改,但可以减少一些复杂性。external_id
即使已经编写了要插入的应用程序代码,即使您包含“旧”值,您最多也可能需要调用该列external_id
。
这样可以使新结构变为:
PkId AS AS COALESCE(old_or_external_id, new_id, -1) PERSISTED NOT NULL,
old_or_external_id INT NULL, -- values from existing record OR passed in from app
new_id INT IDENTITY(2000000, 1) NOT NULL
现在,您仅增加了每行8个字节,而不是12个字节(假设您没有使用该SPARSE
选项或数据压缩)。而且您不需要更改任何代码,T-SQL或App代码。
第二
继续简化的过程,让我们看一下剩下的内容:
- 该
old_or_external_id
列要么已经具有值,要么将从应用程序中获得新值,或者保留为NULL
。
- 该
new_id
总会有产生新的价值,但如果将只使用该值old_or_external_id
列NULL
。
从来没有机会需要同时在old_or_external_id
和中使用值new_id
。是的,有时两列都具有new_id
作为的值IDENTITY
,但是这些new_id
值会被忽略。同样,这两个字段是互斥的。所以现在怎么办?
现在,我们可以看看为什么我们首先需要external_id
。考虑到可以使用插入到IDENTITY
列中SET IDENTITY_INSERT {table_name} ON;
,因此您可以完全不进行任何模式更改,而只需修改应用程序代码以将INSERT
语句/操作包装在SET IDENTITY_INSERT {table_name} ON;
and SET IDENTITY_INSERT {table_name} OFF;
语句中即可。然后,您需要确定将IDENTITY
列重置为哪个起始范围(对于新生成的值),因为它必须远高于应用代码将插入的值,因为插入较高的值将导致下一个自动生成的值大于当前的MAX值。但是,您始终可以插入一个低于IDENT_CURRENT值的值。
将old_or_external_id
和new_id
列合并也不会增加自动生成的值与应用生成的值之间出现重叠值情况的机会,因为使用2列甚至3列的目的是将它们合并为主键值,这些始终是唯一的值。
使用这种方法,您只需要:
第二部分,B部分
上面直接指出的方法的一种变化是使App代码插入从-1开始并从该位置向下的值。这使这些IDENTITY
值成为唯一上升的值。这样做的好处是,您不仅不使架构复杂化,而且还不必担心会遇到重叠的ID(如果应用程序生成的值进入了新的自动生成的范围)。仅当您尚未使用负ID值时(这是一个选择),而且人们很少在自动生成的列上使用负值,因此在大多数情况下这应该是可能的)。
使用这种方法,您只需要:
在这里,您仍然需要执行IDENTITY_INSERT
,但是:您无需添加任何新列,无需“播种”任何IDENTITY
列,并且将来没有重叠的风险。
第二部分,第3部分
这种方法的最后一种变体是可能换出IDENTITY
列,而使用Sequences。采用这种方法的原因是能够使应用代码插入以下值:正数,高于自动生成的范围(不低于),并且不需要SET IDENTITY_INSERT ON / OFF
。
使用这种方法,您只需要:
- 使用CREATE SEQUENCE创建序列
使用NEXT VALUE FOR函数将IDENTITY
列复制到不具有该IDENTITY
属性但具有DEFAULT
约束的新列:
PkId INT PRIMARY KEY CONSTRAINT [DF_TableName_NextID] DEFAULT (NEXT VALUE FOR...)
这会为每行增加0个字节,而不是8个甚至12个字节。
- 应用程序生成的值的起始范围将远远超出您认为自动生成的值所能达到的范围。
- 在
SET IDENTITY_INSERT {table_name} ON;
和SET IDENTITY_INSERT {table_name} OFF;
语句中包装应用代码INSERT 。
但是,由于要求带有一个SCOPE_IDENTITY()
或两个@@IDENTITY
仍能正常运行的代码的要求,因此当前无法选择切换到Sequences,因为看来对于Sequences :-(。