第一
你可能并不需要所有三列: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 :-(。