有没有办法查看 SQL Server 2012 如何定义新身份?

Sha*_*eis 7 sql-server database-internals identity sql-server-2012

关于Microsoft SQL Server 2012 (SP1) - 11.0.3000.0 (X64),有没有办法查看 SQL Server 使用什么机制来计算由创建的表的新标识值SELECT INTO


样本数据

-- Create our base table
CREATE TABLE dbo.A
(A_ID INT IDENTITY(1, 1),
x1 INT,
noise1 int DEFAULT 1,
noise2 char(1) DEFAULT 'S',
noise3 date DEFAULT GETUTCDATE(),
noise4 bit DEFAULT 0);

-- Create random data between the range of [0-3]
INSERT INTO dbo.A(x1)
SELECT s1000.n FROM
( SELECT TOP (10) n = 1 FROM sys.columns) AS s10 -- 10
CROSS JOIN
( SELECT TOP (10) n = 1 FROM sys.columns) AS s100 -- 10 * 10
CROSS JOIN
( SELECT TOP (10) n = ABS(CHECKSUM(NEWID())) % 4 FROM sys.columns) AS s1000; -- 100 * 10

SELECT * FROM dbo.A ORDER BY A_ID DESC;
Run Code Online (Sandbox Code Playgroud)

就我而言,结果是:

dbo.A 结果

并运行

DBCC CHECKIDENT('A')
Run Code Online (Sandbox Code Playgroud)

返回

检查标识信息:当前标识值“1000”,当前列值“1000”。


创建新表

当我们选择一个子集到一个新表中时,它会创建一个新的当前标识值:

SELECT * INTO #temp FROM dbo.A WHERE x1 = 0;
Run Code Online (Sandbox Code Playgroud)

这个新表的标识列中的最大值是 998,当我们检查它的下一个标识时:

DBCC CHECKIDENT('#temp')
Run Code Online (Sandbox Code Playgroud)

检查标识信息:当前标识值'998',当前列值'998'。


如何?

如何将这些标识值插入到新表中,并将最高值正确设置为新表的当前标识值?

Pau*_*ite 14

SELECT INTO 有两个阶段(在执行计划中不可见)。

首先,它创建一个表,该表与用于创建它的查询的元数据相匹配。这发生在系统事务中,因此(空)创建的表将继续存在,即使该表SELECT INTO包含在回滚的用户事务中。在第一阶段结束时,我们有一个空表,其中包含一个名为A_ID的列,具有标识属性。

第二阶段对第一阶段创建的表执行插入操作。此插入是通过identity_insert语义完成的,因此源表中的标识值最终会在目标中保持不变。实际插入最高值被设置为最后使用的值。您可以使用IDENT_CURRENTsys.indentity_columns来查看它。

不相关但半有趣的事实:在创建表和开始执行插入之间有一个小窗口,其中查询中的源表不受模式稳定性锁的保护。如果并发进程碰巧更改了源表的架构(在创建目标之后,但在插入开始之前),则会引发错误 539:

创建目标表后架构已更改。重新运行 Select Into 查询。