1:每个表只能创建一个标识列。

如下测试所示,如果表中有一个标识列,新增一个标识列就会遇到错误“Multiple identity columns specified for table 'TEST'. Only one identity column per table is allowed.“

CREATE TABLE dbo.TEST

(

    ID        INT IDENTITY(1,1) ,

    NAME        VARCHAR(32)

);

 

ALTER TABLE dbo.TEST ADD  ID1 INT IDENTITY(10,1)

2:标识列不能被更新。

如果你更新标识列,就会遇到类似下面这样的错误。

Cannot update identity column 'xxx'.

 

3:SQL Server不能通过ALTER语句修改标识列的increment值大小。

如果非要调整标识列的increment值大小,只能通过重建表来实现。如果想通过增加列或删除列的方法,非常麻烦。很多情况下也是不行的。例如,有些情况下需要你对新增的自增标识列更新数据才能保证数据一致性。还有一种非常规方法就是修改系统基表sys.syscolpars。这个后续整理一篇。

 

4:SQL Server不能通过ALTER语句修改表标识列的SEED的大小但是可以DBCC CHECKIDENT命令调整。SEED可以调大也可以调小,但是有一些限制!

#查看某个表中的自增列当前的值:

DBCC CHECKIDENT (TableName,NORESEED)

#调整标识列的当前值(SEED)为50

DBCC CHECKIDENT('dbo.TEST', RESEED, 50);

通过DBCC CHECKIDENT命令调整SEED值大小,也是有限制的,如下实验所示:

USE AdventureWorks2014;

GO

IF EXISTS (SELECT 1 FROM sys.objects WHERE type='U' AND name='TEST')

BEGIN

    DROP TABLE test;

END

GO

CREATE TABLE dbo.TEST

(

    ID        INT IDENTITY(1,1) ,

    NAME    VARCHAR(32)

);

 

INSERT INTO dbo.TEST

        (  NAME )

SELECT  'K1' UNION ALL

SELECT  'K2' UNION ALL

SELECT  'K3' UNION ALL

SELECT  'K4' UNION ALL

SELECT  'K5' UNION ALL

SELECT  'K6';

 

SET IDENTITY_INSERT dbo.TEST ON;

GO

INSERT INTO dbo.TEST

        ( ID, NAME )

SELECT 13, 'k13';

GO

SET IDENTITY_INSERT dbo.TEST OFF;

GO

 

DBCC CHECKIDENT(test)

 

DBCC CHECKIDENT('test', RESEED ,9);

 

 

 

INSERT INTO dbo.TEST

        (  NAME )

SELECT  'K9'  UNION ALL

SELECT  'K10' UNION ALL

SELECT  'K11' UNION ALL

SELECT  'K12' UNION ALL

SELECT  'K13' ;

SELECT * FROM dbo.TEST;

如果你修改一下表结构,标识列为主键或有唯一约束的话,

CREATE TABLE dbo.TEST

(

    ID        INT IDENTITY(1,1) PRIMARY KEY,

    NAME      VARCHAR(32)

);

那么上面脚本运行到插入数据时就会报主键冲突。错误如下所示:

Msg 2627, Level 14, State 1, Line 38

Violation of PRIMARY KEY constraint 'PK__TEST__3214EC2731C41DF1'. Cannot insert duplicate key in object 'dbo.TEST'. The duplicate key value is (13).

那么接下来,我们将上面的脚本稍微调整一下,你会看到完全不同的结果。如下所示:

USE AdventureWorks2014;

GO

IF EXISTS (SELECT 1 FROM sys.objects WHERE type='U' AND name='TEST')

BEGIN

    DROP TABLE test;

END

GO

CREATE TABLE dbo.TEST

(

    ID        INT IDENTITY(1,1) ,

    NAME      VARCHAR(32)

);

 

INSERT INTO dbo.TEST

        (  NAME )

SELECT  'K1' UNION ALL

SELECT  'K2' UNION ALL

SELECT  'K3' UNION ALL

SELECT  'K4' UNION ALL

SELECT  'K5' UNION ALL

SELECT  'K6';

 

SET IDENTITY_INSERT dbo.TEST ON;

GO

INSERT INTO dbo.TEST

        ( ID, NAME )

SELECT 13, 'k13';

GO

SET IDENTITY_INSERT dbo.TEST OFF;

GO

 

 

 

DBCC CHECKIDENT('test', RESEED ,9);

GO

DBCC CHECKIDENT(test);

GO

 

INSERT INTO dbo.TEST

        (  NAME )

SELECT  'K9'  UNION ALL

SELECT  'K10' UNION ALL

SELECT  'K11' UNION ALL

SELECT  'K12' UNION ALL

SELECT  'K13' ;

SELECT * FROM dbo.TEST;

这个是实验测试时意外发现的一个问题,当时,它导致我得出不同的实验结果,结论也搞错了,问题出在DBCC CHECKIDENT (table_name),如果表的当前标识值小于标识列中存储的最大标识值,则使用标识列中的最大值对其进行重置。我使用DBCC CHECKIDENT(test)本意是来查看标识列的当前值,所以正确的做法应该用DBCC CHECKIDENT(test, NORESEED)这条命令。其实这里也衍生了一个问题,由于可以人为调整SEED的值,所以标识列的值的唯一性,必须通过“PRIMARY KEY”或“UNIQUE”约束或者通过“UNIQUE”索引来实现。将字段设置为标识列并不能保证值的唯一值。

 

4: 不能通过ALTER语句将已经存在的一个字段改为标识列

CREATE TABLE dbo.TEST

(

    ID        INT ,

    NAME      VARCHAR(32)

);

 

--这种语法是不允许的

ALTER TABLE dbo.TEST  ALTER COLUMN ID IDENTITY(10,1) 

5:在内存优化表中,种子和增量必须分别设置为 1、1。 将种子或增量设置为 1 以外的值会导致以下错误:内存优化表不支持使用 1 以外的种子和增量值。另外,必须同时指定种子和增量,或者二者都不指定。 如果二者都未指定,则取默认值 (1,1)

 

6:如果事务回滚会导致标识列跳号。如下实验所示,这种现象和Oracle、MySQL数据库的行为一致。

--事务回滚导致标识列自增跳号

INSERT INTO dbo.TEST

        (  NAME )

SELECT  'K1' UNION ALL

SELECT  'K2' UNION ALL

SELECT  'K3' UNION ALL

SELECT  'K4' UNION ALL

SELECT  'K5' UNION ALL

SELECT  'K6';

 

BEGIN TRAN

INSERT INTO dbo.TEST

        (  NAME )

SELECT  'K7';

ROLLBACK TRAN;

 

INSERT INTO dbo.TEST

        (  NAME )

SELECT  'KKK';

 

SELECT * FROM dbo.TEST;

7: 事务内部,可能出现标识列的跳号。

如下实验所示:

USE AdventureWorks2014;

GO

IF EXISTS (SELECT 1 FROM sys.objects WHERE type='U' AND name='TEST_TRAN')

BEGIN

    DROP TABLE TEST_TRAN;

END

GO

CREATE TABLE dbo.TEST_TRAN

(

    ID        INT IDENTITY(1,1) PRIMARY KEY,

    TRN_NAME    VARCHAR(32)

);

在会话1和会话2同时执行下面SQL语句,模拟并发的事务。

--会话1:

DECLARE @row_index INT;

 

SET @row_index =1;

 

BEGIN TRAN

WHILE @row_index <=10

BEGIN

    INSERT INTO TEST_TRAN

    VALUES('TRANS_1');

    SET @row_index +=1;

    WAITFOR DELAY '00:00:01';

END

COMMIT TRAN;

--会话2

DECLARE @row_index INT;

 

SET @row_index =1;

 

BEGIN TRAN

WHILE @row_index <=10

BEGIN

    INSERT INTO TEST_TRAN

    VALUES('TRANS_2');

    SET @row_index +=1;

 

    WAITFOR DELAY '00:00:01';

END

COMMIT TRAN;

执行完上面脚本后,我们可以看到在并发情况下,同一事务内可能出现跳号。这个可以称其为“逻辑跳号”

7:数据库实例非正常重启(崩溃,故障转移或关闭而导致SQL Server服务意外重启),出现标识列的跳号

关于这个,官方文档有简单介绍。

Consecutive values after server restart or other failures -SQL Server might cache identity values for performance reasons and some of the assigned values can be lost during a database failure or server restart. This can result in gaps in the identity value upon insert. If gaps are not acceptable then the application should use its own mechanism to generate key values. Using a sequence generator with the NOCACHE option can limit the gaps to transactions that are never committed.

个人简单测试了一下,发现在SQL Server 2012在服务器非正常重启(崩溃,故障转移或关闭而导致SQL Server服务意外重启)后会出现跳号(identity column jump)情况。可以通过启用踪标志272解决这个问题(参考下面链接),SQL Server 2014下测试时,也是如此。注意:如果正常重启SQL Server实例,并不会出现这种情况。这个跟ORACLE SEQUENCE跳号总结中的情况有点类似。

https://www.dfarber.com/computer-consulting-blog/articles/how-to-solve-identity-problem-in-sql-2012/

https://blog.sqlauthority.com/2017/03/24/sql-server-jump-identity-column-restart/

https://blog.sqlauthority.com/2018/01/24/sql-server-identity-jumping-1000-identity_cache/

https://www.codeproject.com/Tips/668042/SQL-Server-2012-Auto-Identity-Column-Value-Jump-Is

个人测试,在任务管理器,杀掉SQL Server的进程后,发现标识列跳号的大小为1000,根据上面博客资料,标识列跳号的多少还跟标识列的数据类型有关。

不过在SQL Server 2017,引入了新特性IDENTITY_CACHE来解决这个问题!

按照网上搜索的资料来看,踪标志272让SQL Server使用以前的代码来实现标识列的功能。

That flag sets SQL 2012 back to the prior code for IDENTITY fields. However, unless you are actually running out of numbers, there is no reason to use that flag. IDENTITY fields are unique, not sequential. You probably need to rethink your method.

那么我们想搞清楚标识列的下一个值保存在哪里呢? SQL Server数据库有个系统视图sys.identity_columns可以查看某个表的标识列的当前值和下一个值。

SELECT  name ,

        is_identity ,

        seed_value ,

        increment_value ,

        last_value

FROM    sys.identity_columns

WHERE   object_id = OBJECT_ID('TEST');

但是 sys.identity_columns是一个系统视图,它的数据来自sys.syscolpars,而视图的字段last_value的值是通过内置函数IdentityProperty计算出来的

SET QUOTED_IDENTIFIER ON

SET ANSI_NULLS ON

GO

CREATE VIEW sys.identity_columns AS

    SELECT object_id = id,

        name = name,

        column_id = colid,

        system_type_id = xtype,

        user_type_id = utype,

        max_length = length,

        precision = prec,

        scale = scale,

        collation_name = convert(sysname,CollationPropertyFromId(collationid,'name')),

        is_nullable = sysconv(bit, 1 - (status & 1)),     -- CPM_NOTNULL

        is_ansi_padded = sysconv(bit, status & 2),     -- CPM_NOTRIM

        is_rowguidcol = sysconv(bit, status & 8),     -- CPM_ROWGUIDCOL

        is_identity = sysconv(bit, status & 4),         -- CPM_IDENTCOL

        is_filestream = sysconv(bit, status & 32),     -- CPM_FILESTREAM

        is_replicated = sysconv(bit, status & 0x20000),     -- CPM_REPLICAT

        is_non_sql_subscribed = sysconv(bit, status & 0x40000),     -- CPM_NONSQSSUB

        is_merge_published = sysconv(bit, status & 0x80000),         -- CPM_MERGEREPL

        is_dts_replicated = sysconv(bit, status & 0x100000),         -- CPM_REPLDTS

        is_xml_document = sysconv(bit, 0),

        xml_collection_id = sysconv(int, 0),

        default_object_id = sysconv(int, 0),

        rule_object_id = sysconv(int, 0),

        seed_value = IdentityProperty(id, 'SeedValue'),

        increment_value = IdentityProperty(id, 'IncrementValue'),

        last_value = IdentityProperty(id, 'LastValue'),

        is_not_for_replication = sysconv(bit, status & 0x10000),    -- CPM_ID_REPL

        is_computed = sysconv(bit, status & 16),            -- CPM_COMPUTED                

        sysconv(bit, 0) as is_sparse,

        sysconv(bit, 0) as is_column_set

    FROM sys.syscolpars

    WHERE number = 0    -- SOC_COLUMN

        AND (status & 4) = 4     -- CPM_IDENTCOL

        AND has_access('CO', id) = 1

 

GO

无法获取系统内置函数(built-in function)的定义,所以无法进一步分析标识列是如何保存last_value的,但是个人猜测可能跟系统基表sys.syscolpars的idtval字段有关系。DAC模式下查询跟踪,你会发现标识列ID变化后,idtval字段的值也变化了。

新建三个表,标识列的自增值分别为1、2、3

CREATE TABLE test1(id INT IDENTITY(1,1), name VARCHAR(10))

CREATE TABLE test2(id INT IDENTITY(1,2), name VARCHAR(10))

CREATE TABLE test3(id INT IDENTITY(1,3), name VARCHAR(10))

8:TRUNCATE表后,标识列的当前值会变为1

9:与标识列相关的系统函数的区别。

SELECT IDENT_CURRENT('dbo.TEST_TRAN');

SELECT IDENT_INCR('dbo.TEST_TRAN');

SELECT IDENT_SEED('dbo.TEST_TRAN')

SELECT SCOPE_IDENTITY();

SELECT @@IDENTITY;

IDENT_CURRENT 类似于SQL Server 2000 (8.x)的标识函数 SCOPE_IDENTITY 和 @@IDENTITY。 这三个函数都返回最后生成的标识值。 但是,上述每个函数中定义的“最后”的作用域和会话有所不同**:

·         IDENT_CURRENT 返回为某个会话和用域中的指定表生成的最新标识值。

·         @@IDENTITY 返回为跨所有作用域的当前会话中的任何表生成的最后一个标识值。

·         SCOPE_IDENTITY 返回为当前会话和当前作用域中的某个表生成的最新标识值。

如果 IDENT_CURRENT 值为 NULL(因为表从未包含行或已被截断),IDENT_CURRENT 函数将返回种子值。

参考资料:

https://docs.microsoft.com/zh-cn/sql/t-sql/statements/create-table-transact-sql-identity-property?view=sql-server-ver15

https://www.dfarber.com/computer-consulting-blog/articles/how-to-solve-identity-problem-in-sql-2012/

https://www.codeproject.com/Tips/668042/SQL-Server-2012-Auto-Identity-Column-Value-Jump-Is

SQL Server解惑——标识列的限制和跳号现象的更多相关文章

  1. SQL Server修改标识列方法(备忘)

    原文:SQL Server修改标识列方法(备忘) SQL Server修改标识列方法 ----允许对系统表进行更新 exec sp_configure 'allow updates',1 reconf ...

  2. [SQL Server]关于标识列,标识从1开始计数的的方法

    DBCC CHECKIDENT ('表名',  RESEED, 0) //从30开始 DBCC  CHECKIDENT  (jobs,  RESEED,  30)

  3. SQL SERVER FOR 多列字符串连接 XML PATH 及 STUFF

    原文:SQL SERVER FOR 多列字符串连接 XML PATH 及 STUFF 本来用 Writer 写一篇关于一列多行合并的博客来的,结果快写完了时候,在一个插入代码时候,崩了,重新打开,居然 ...

  4. SQL Server 2014 聚集列存储

    SQL Server 自2012以来引入了列存储的概念,至今2016对列存储的支持已经是非常友好了.由于我这边线上环境主要是2014,所以本文是以2014为基础的SQL Server 的列存储的介绍. ...

  5. SQL Server分区键列必须是主键一部分

    SQL Server分区键列必须是主键一部分. 必须把分区列包含在主键/唯一约束/唯一索引的键列中. USE tempdb GO -- 测试表 CREATE TABLE dbo.tb( id int, ...

  6. sql server 自增列,值突然增大1000的情况

    sql server 自增列,值突然增大1000的情况   解决方法: 1 打开配置管理器2左面点击sql服务3右面 右键点击SQL Server(MSSQLSERVER) 4点击 启动参数5 在参数 ...

  7. sql server 某一列求和

    sql server 某一列求和 SELECT 患者来源,设备类型,检查部位,设备名称,convert(char(10),STUDY_DATE,121) as 日期, count(distinct 就 ...

  8. SQL Server解惑——为什么你的查询结果超出了查询时间范围

    原文:SQL Server解惑--为什么你的查询结果超出了查询时间范围 废话少说,直接上SQL代码(有兴趣的测试验证一下),下面这个查询语句为什么将2008-11-27的记录查询出来了呢?这个是同事遇 ...

  9. SQL Server自增长列插入指定值 -- SET IDENTITY_INSERT ON|OFF(转)

    想要将值插入到自动编号(或者说是标识列,IDENTITY)中去,需要设定 SET IDENTITY_INSERT 示例: 1.首先建立一个有标识列的表:CREATE TABLE products (i ...

随机推荐

  1. log4j2 自动删除过期日志文件配置及实现原理解析

    日志文件自动删除功能必不可少,当然你可以让运维去做这事,只是这不地道.而日志组件是一个必备组件,让其多做一件删除的工作,无可厚非.本文就来探讨下 log4j 的日志文件自动删除实现吧. 0. 自动删除 ...

  2. 【NOIP2013】火柴排队 题解(贪心+归并排序)

    前言:一道水题. ----------------------- 题目链接 题目大意:给出数列$a_i$和$b_i$,问使$\sum_{i=1}^n (a_i-b_i)^2$最小的最少操作次数. 首先 ...

  3. 符合AUTOSAR(AP&CP)的嵌入式系统和软件设计工具

    AUTOSAR Builder功能介绍 AUTOSAR Builder 是达索旗下一种基于 Eclipse 的开放性.可扩展工具套件,用于设计和开发符合 AUTOSAR 标准的系统和软件.最新版本20 ...

  4. Eclipse RCP:多平台部署

    1 问题 在使用Eclipse RCP IDE进行开发时,它自带的PDE(插件开发环境)工具仅能够导出相同平台的部署包,比如win32的仅能导出win32的,linux64仅能够导出linux64的. ...

  5. “随手记”开发记录day20

    练习软件的展示,尽量将软件全方面的展示给大众,希望不要像上次一样有许多遗漏的地方,让其他团队以为我们的软件没有完善的功能.

  6. 解惑4:java是值传递还是引用传递

    一.概述 曾经纠结了很久java的参数传递方式是什么样的,后面粗略的了解了一鳞半爪以后有了大概的印象:"传参数就是值传递,传对象就是引用传递",后面进一步查找了相关资料和文章以后, ...

  7. Docker 启动 Nginx

    Docker 启动 Nginx 拉取镜像 docker pull nginx:1.17.9 启动步骤 # 创建 nginx 目录 mkdir -p /usr/local/nginx && ...

  8. 2020-08-08:有一批气象观测站,现需要获取这些站点的观测数据,并存储到 Hive 中。但是气象局只提供了 api 查询,每次只能查询单个观测点。那么如果能够方便快速地获取到所有的观测点的数据?

    福哥答案2020-08-08: 参考答案:A.通过shell 或python 等调用api,结果先暂存本地,最后将本地文件上传到 Hive 中.B.通过 datax 的 httpReader 和 hd ...

  9. C#LeetCode刷题之#700-二叉搜索树中的搜索(Search in a Binary Search Tree)

    问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/4102 访问. 给定二叉搜索树(BST)的根节点和一个值. 你需要 ...

  10. YAML简要入门

    这是一篇简单的YAML入门教程,目的是让你知晓什么YAML,以及YAML的基础语法.方便接下来学习如何使用Golang解析YAML.如果想获得更多YAML的知识,请查看http://yaml.org ...