原文:10. IDENTITY属性使用小结

从SQL Server 2012开始有了Sequence,简单用列如下:

  1. CREATE SEQUENCE TestSeq
  2. START WITH 1
  3. INCREMENT BY 1 ;
  4.  
  5. SELECT NEXT VALUE FOR TestSeq AS NextValue;

在这之前,表中生成序列号大多都是借助IDENTITY列属性,当然也有一些时候,是在自定义表中,自己维护序列号。

一. 创建IDENTITY列

  1. if OBJECT_ID('test','U') is not null
  2. drop table test
  3. GO
  4. create table test(id int identity, c1 char(1))
  5. insert test values('a');
  6. insert test values('b');
  7. select * from test

1. 没有指定IDENTITY(seed ,increment),默认就是 IDENTITY(1, 1),效果同如下语句

  1. create table test(id int identity(1,1), c1 char(1))

2. 通过函数或者系统视图,都可以查看是否为IDENTITY列

  1. SELECT COLUMNPROPERTY(OBJECT_ID('test'),'id','IsIdentity') AS is_identity
  2.  
  3. select object_name(object_id) as table_name, is_identity,*
  4. from sys.columns
  5. where object_id=object_id('test')
  6. --and is_identity=1

3. 重置IDENTITY列的初始值,通常在数据删除/归档后进行

  1. DELETE test
  2. DBCC CHECKIDENT('test', RESEED, 1)
  3. DBCC CHECKIDENT('test', NORESEED)
  4.  
  5. --TRUNCATE表后会自动重置IDENTITY
  6. TRUNCATE TABLE test
  7. DBCC CHECKIDENT('test', NORESEED)

二. 获取IDENTITY列值
插入了数据,有时还需要获取刚才生成的序列值另作他用,返回给前端也好,或者插入其他将来需要关联的表。

记得曾经有个面试题:假设当前表IDENTITY列最大值为N,在存储过程中,对这个表插入1行数据,获取到的IDENTITY列值有时小于或者大于N+1,可能是什么原因?

获取IDENTITY列值有三种方式:
(1) IDENT_CURRENT( 'table_name' ) 返回为任何会话和任何作用域中的特定表最后生成的标识值。
(2) @@IDENTITY 返回为当前会话的所有作用域中的任何表最后生成的标识值。
(3) SCOPE_IDENTITY() 返回为当前会话和当前作用域中的任何表最后生成的标识值。

IDENT_CURRENT( 'table_name' ) 针对特定表,是全局的。@@IDENTITY和SCOPE_IDENTITY()针对所有表,区别在于作用域,也就是上下文:
(1) 如果当前INSERT语句上有函数,触发器等(不同作用域的)对象返回的IDENTITY值,那么@@IDENTITY会取所有表上的最后1个,而不是当前表上的;

(2) SCOPE_IDENTITY()会取当前作用域所有表上最后1个IDENTITY值,被调用的函数,触发器已经超出了作用域/上下文。所以在使用INSERT后,接着使用SCOPE_IDENTITY()获取IDENTITY列值,就不会有问题了:

  1. insert test values('z');
  2. select SCOPE_IDENTITY() as curr_value

一个GO语句/批处理,也是一个上下文的分界点,但是SQL语句是顺序执行的,所以一个会话里,只要在INSERT之后用SCOPE_IDENTITY()来获取IDENTITY值是没问题的。

三. 修改IDENTITY列值/属性
1. 对已存在的列增加/删除IDENTITY属性

  1. if OBJECT_ID('t_id') is not null
  2. drop table t_id
  3. GO
  4. create table t_id(id int,c1 char(1))
  5.  
  6. insert into t_id
  7. select 1,'a' union all
  8. select 2,'b'
  9.  
  10. alter table t_id alter column id int identity(1,2)
  11. /*
  12. Msg 156, Level 15, State 1, Line 2
  13. Incorrect syntax near the keyword 'identity'.
  14. */

直接修改列属性会报错,IDENTITY属性只能伴随着列增加/删除。

(1) 利用中间表
在SSMS界面上设计表(SSMS/Tables/Design),可以直接增加/删除列上的IDENTITY属性,如果生成脚本看看的话(右击编辑框/工具栏/菜单栏),可以发现SSMS是利用了中间表,并非在原表直接修改属性。

表上有约束,索引等对象时,脚本会更加繁杂些。示例如下图:

如果出现如下错误:
Saving changes is not permitted. The changes that you have made require the following tables to be dropped and re-created. You have either made changes to a table that can't be re-created or enabled the option Prevent saving changes that require the table to be re-created.

是因为SSMS里有个选项没设置,SQL Server认为有删除/重建表的脚本不安全,所以默认关闭了,需要手动开启一下,去掉那个勾:

对表上已存在列添加IDENTITY属性,生成的脚本如下:

  1. BEGIN TRANSACTION
  2. SET QUOTED_IDENTIFIER ON
  3. SET ARITHABORT ON
  4. SET NUMERIC_ROUNDABORT OFF
  5. SET CONCAT_NULL_YIELDS_NULL ON
  6. SET ANSI_NULLS ON
  7. SET ANSI_PADDING ON
  8. SET ANSI_WARNINGS ON
  9. COMMIT
  10. BEGIN TRANSACTION
  11. GO
  12. CREATE TABLE dbo.Tmp_t_id
  13. (
  14. id int NOT NULL IDENTITY (1, 1),
  15. c1 char(1) NULL
  16. ) ON [PRIMARY]
  17. GO
  18. ALTER TABLE dbo.Tmp_t_id SET (LOCK_ESCALATION = TABLE)
  19. GO
  20. SET IDENTITY_INSERT dbo.Tmp_t_id ON
  21. GO
  22. IF EXISTS(SELECT * FROM dbo.t_id)
  23. EXEC('INSERT INTO dbo.Tmp_t_id (id, c1)
  24. SELECT id, c1 FROM dbo.t_id WITH (HOLDLOCK TABLOCKX)')
  25. GO
  26. SET IDENTITY_INSERT dbo.Tmp_t_id OFF
  27. GO
  28. DROP TABLE dbo.t_id
  29. GO
  30. EXECUTE sp_rename N'dbo.Tmp_t_id', N't_id', 'OBJECT'
  31. GO
  32. COMMIT

对表上已存在列删除IDENTITY属性,生成的脚本如下:

  1. BEGIN TRANSACTION
  2. SET QUOTED_IDENTIFIER ON
  3. SET ARITHABORT ON
  4. SET NUMERIC_ROUNDABORT OFF
  5. SET CONCAT_NULL_YIELDS_NULL ON
  6. SET ANSI_NULLS ON
  7. SET ANSI_PADDING ON
  8. SET ANSI_WARNINGS ON
  9. COMMIT
  10. BEGIN TRANSACTION
  11. GO
  12. CREATE TABLE dbo.Tmp_t_id
  13. (
  14. id int NOT NULL,
  15. c1 char(1) NULL
  16. ) ON [PRIMARY]
  17. GO
  18. ALTER TABLE dbo.Tmp_t_id SET (LOCK_ESCALATION = TABLE)
  19. GO
  20. IF EXISTS(SELECT * FROM dbo.t_id)
  21. EXEC('INSERT INTO dbo.Tmp_t_id (id, c1)
  22. SELECT id, c1 FROM dbo.t_id WITH (HOLDLOCK TABLOCKX)')
  23. GO
  24. DROP TABLE dbo.t_id
  25. GO
  26. EXECUTE sp_rename N'dbo.Tmp_t_id', N't_id', 'OBJECT'
  27. GO
  28. COMMIT

(2) 利用中间列
对表上已存在列删除IDENTITY属性

  1. if OBJECT_ID('t_id') is not null
  2. drop table t_id
  3. GO
  4. create table t_id(id int identity(1,1),c1 char(1))
  5.  
  6. insert into t_id
  7. select 'a' union all
  8. select 'b'
  9. select * from t_id
  10. SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')
  11.  
  12. --在表上新增一个列,把IDENTITY列值复制过去
  13. alter table t_id add id_new int
  14. GO
  15. update t_id set id_new = id
  16.  
  17. --删除原来的列,并重命名新增列
  18. alter table t_id drop column id
  19. exec sp_rename 't_id.id_new','id'
  20. select * from t_id
  21. SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')

对表上已存在列添加IDENTITY属性,用中间列的方式不太可行,因为IDENTITY列不接受UPDATE,新增的IDENTITY列无法直接复制原id的值,还得借助中间表,但如果不需要原来id的值,那么可以:

  1. if OBJECT_ID('t_id') is not null
  2. drop table t_id
  3. GO
  4. create table t_id(id int,c1 char(1))
  5.  
  6. insert into t_id
  7. select 1,'a' union all
  8. select 3,'b'
  9. select * from t_id
  10. SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')
  11.  
  12. --在表上新增一个IDENTITY列,不复制原来的ID
  13. alter table t_id add id_new int identity(1,1) not null
  14.  
  15. --删除原来的列,并重命名新增列
  16. alter table t_id drop column id
  17. exec sp_rename 't_id.id_new','id'
  18. select * from t_id
  19. SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')

2. 在IDENTITY列上做增删改操作(DML)
(1) 删除操作没有问题,直接DELETE即可

  1. delete test where id = 2

(2) 如果要显式INSERT某个值,需要开启IDENTITY_INSERT这个SESSION级的选项

  1. set IDENTITY_INSERT test on;
  2. insert test(id,c1) values(3,'c');
  3. set IDENTITY_INSERT test off;
  4. select * from test

(3) 如果要UPDATE IDENTITY列值,无论是否开启IDENTITY_INSERT这个选项都无法更新

  1. set IDENTITY_INSERT test on;
  2. update test set id = 10 where id = 1
  3. set IDENTITY_INSERT test off;
  4. /*
  5. Msg 8102, Level 16, State 1, Line 1
  6. Cannot update identity column 'id'.
  7. */

非要修改的话,就得借助中间表,在不含IDENTITY属性的中间表里做完UPDATE,然后再把数据导回来。中间表可参考上面的脚本。

3. IDENTITY列属性复制
(1) 直接从单表SELECT INTO table_name,原表其他约束,索引等等都不会被复制,但是IDENTITY属性会被复制。

  1. select * into test2 from test
  2. select * from test2
  3. select columnproperty(OBJECT_ID('test'),'id','IsIdentity')
  4. select columnproperty(OBJECT_ID('test2'),'id','IsIdentity')

(2) 如果有IDENTITY属性的表和其他表JOIN,那么IDENTITY属性不会被复制。

  1. select a.* into test3
  2. from test a inner join sys.objects b
  3. on a.id = b.object_id
  4.  
  5. select * from test3
  6. select columnproperty(OBJECT_ID('test3'),'id','IsIdentity')

假如复制表时,不想要IDENTITY属性,正好可以利用一下这个特点,如下:

  1. select a.* into test4
  2. from test a inner join sys.objects b
  3. on 1=2

(3) 如果用SELECT INTO table_name导数据时,FROM子句有多表关联,且想要保留IDENTITY属性,这时可以用INSERT,并考虑使用TABLOCK提示

  1. if OBJECT_ID('test5','U') is not null
  2. drop table test5
  3. GO
  4.  
  5. create table test5(id int identity, c1 char(1))
  6. select * from test5
  7. GO
  8.  
  9. set IDENTITY_INSERT test5 on;
  10. insert into test5 WITH(TABLOCK) (id,c1)
  11. select a.* from test a inner join test2 b on a.id = b.id
  12. set IDENTITY_INSERT test5 off;
  13.  
  14. select * from test5
  15. select columnproperty(OBJECT_ID('test5'),'id','IsIdentity')

这里使用了WITH(TABLOCK)选项,在SIMPLE或者BULK_LOGGED恢复模式下,SELECT…INTO table_name和INSERT INTO table_name WITH(TABLOCK)都能最小化日志。

4. 借助SWITCH来处理IDENTITY属性,推荐
同样也是利用中间表,上面的几个列子都使用了INSERT,这里使用SWITCH,不再有数据倒来倒去的开销,需要SQL Server 2008及以上版本,能比较有效地同时解决上面的3个问题:
(1) 不能直接对表上现有列增加/删除IDENTITY属性;
(2) 不能直接更新IDENTITY列;
(3) 复制表时,有选择的复制IDENTITY列属性(多表关联,对关联后的表做SWITCH以实现);

  1. CREATE TABLE Temp1
  2. (
  3. ID INT IDENTITY(1,1) PRIMARY KEY,
  4. X VARCHAR(10)
  5. )
  6.  
  7. INSERT INTO Temp1
  8. OUTPUT INSERTED.*
  9. SELECT 'Foo' UNION ALL
  10. SELECT 'Bar' UNION ALL
  11. SELECT 'Baz'
  12.  
  13. CREATE TABLE Temp2
  14. (
  15. ID INT PRIMARY KEY,
  16. X VARCHAR(10)
  17. )
  18.  
  19. ALTER TABLE Temp1 SWITCH TO Temp2;
  20. SELECT COLUMNPROPERTY(OBJECT_ID('Temp1'),'id','IsIdentity')
  21. SELECT COLUMNPROPERTY(OBJECT_ID('Temp2'),'id','IsIdentity')
  22.  
  23. INSERT INTO Temp2
  24. OUTPUT INSERTED.*
  25. SELECT 10,'Foo' UNION ALL
  26. SELECT 20,'Bar' UNION ALL
  27. SELECT 5, 'Baz'
  28.  
  29. UPDATE Temp2 SET ID = ID + 1;
  30.  
  31. ALTER TABLE Temp2 SWITCH TO Temp1;
  32. SELECT * FROM Temp2
  33. SELECT * FROM Temp1

另外,从SQL Server 2012开始,如果开发时使用了SEQUENCE,这些IDENTITY列的限制就都不会存在了。

四. IDENTITY函数
这是一个函数,使用时和IDENTITY属性的格式很相似,不过两者没什么关系,纯粹因为名字相同,顺便提一下。

  1. select IDENTITY(int,1,1) as id into #t
  2. from sysobjects
  3.  
  4. select cast(IDENTITY(int,1,1) as varchar(1000)) as id into #t2
  5. from sysobjects
  6. -- can not use expression with identity function directly

IDENTITY函数限制比较多,只能用在SELECT INTO语句里,不能结合表达式使用,而且有了ROW_NUMBER(),IDENTITY函数就更显得不好用了。

10. IDENTITY属性使用小结的更多相关文章

  1. IDENTITY属性使用

    转自:http://www.cnblogs.com/seusoftware/p/3804333.html 一. 获取IDENTITY列值插入了数据,有时还需要获取刚才生成的序列值另作他用,返回给前端也 ...

  2. <NET CLR via c# 第4版>笔记 第10章 属性

    10.1 无参属性 10.1.1 自动实现的属性 10.1.2 合理定义属性 属性可以只读或只写,而字段访问总是可读和可写的(一个例外是 readonly 字段仅在构造器中可写). 属性方法可能抛出异 ...

  3. IT兄弟连 HTML5教程 CSS3属性特效 小结及习题

    本章小结 CSS3新增了许多属性,CSS3样式新增了一种颜色模式rgba用来制作透明色,比CSS的颜色模式多了一个透明度的设置.文字的CSS3特效有文字阴影.文字描边.文字排版和文字省略等.另外,CS ...

  4. SQL修改表结构之添加主键,添加IDENTITY属性

    设计一张表时没有考虑到主键Id及自增长,现又需要,原脚本: SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[F ...

  5. spring security 3中的10个典型用法小结

    spring security 3比较庞大,但功能很强,下面小结下spring security 3中值得 注意的10个典型用法 1)多个authentication-provide可以同时使用 &l ...

  6. Taskaffinity属性使用小结

    TaskAffinity属性小结 最近在项目中用到了TaskAffinity属性,发现这个还是挺有意思,可以用来控制activity所属的任务栈.但同时只设置这一个属性又是不能完成功能的,需要与其它属 ...

  7. JS学习之DOM节点的关系属性封装、克隆节点、Dom中Style常用的一些属性等小结

    JS DOM节点: 在JS DOM中节点的关系被定义为节点的属性: 通常有以下几种节点之间的关系: (这里的关系是所有浏览器都支持的) parentNode    父节点 childNodes     ...

  8. Swift编程语言学习10—— 枚举属性监视器

    属性监视器 属性监视器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性监视器.甚至新的值和如今的值同样的时候也不例外. 能够为除了延迟存储属性之外的其它存储属性加入属性监视器,也能够通过重载 ...

  9. JS获取对象属性名小结

    最近面试遇到问如何获取对象全部属性名的方法,总结一下: 对象属性类型分类: 1.ESMAScript分类 数据类型 又分为可枚举和不可枚举类型 访问器类型 2.上下文分类 原型属性 实例属性 1.列举 ...

随机推荐

  1. IP多播(组播)

    IP多播是实现数据一对多通信的模式.从一个源点传送到多个目的地,数据仅仅拷贝一份.这里说的数据仅仅拷贝一份,是指在每一条须要它的两个点之间,数据仅仅有一份.例如以下图为<计算机网络>(谢希 ...

  2. crm操作观点

    using System;     using Microsoft.Xrm.Sdk;     using Microsoft.Crm.Sdk.Messages; /// <summary> ...

  3. 终结者单身——setAccessible(true)

    首先看一下"传说"Singleton模式 package go.derek; public class Singleton{ public static int times; pr ...

  4. Chapter 1 Securing Your Server and Network(7):禁用SQL Server Browse

    原文:Chapter 1 Securing Your Server and Network(7):禁用SQL Server Browse 原文出处:http://blog.csdn.net/dba_h ...

  5. Server SAN:弄潮儿云计算时代

    最初发表于<程序猿>2014年7每月一次. 4月30日本.Redhat公布1.71十亿收购Ceph开发商Inktank公司,加上之前2011年10月1.36十亿收购Gluster,Redh ...

  6. UVA 11249 - Game(游戏)

    UVA 11249 - Game 题目链接 题意:两堆石头.a和b.每次能取一堆随意数量,或者两堆同一时候取.可是绝对值差不能超过k,最后不能取的人输,问先手能否赢 思路:先如果(a, b)石子,a是 ...

  7. java注意事项演示 地图产生表 演示样本 来自thinking in java 4 20代码的章

    java注意事项演示 地图产生表 演示样本  来自thinking in java 4 20代码的章 thinking in java 4免费下载:http://download.csdn.net/d ...

  8. Android: ADT 23.0.2

    http://pan.baidu.com/s/1gdnBUUJ 版权声明:本文博主原创文章.博客,未经同意不得转载.

  9. U盘启动盘安装Win7/9/10系统攻略

    UltraISO制作U盘启动盘安装Win7/9/10系统攻略 U盘安装好处就是不用使用笨拙的光盘,光盘还容易出现问题,无法读取的问题.U盘体积小,携带方便,随时都可以制作系统启动盘. U盘建议选择8G ...

  10. C++ Web Programming

    一般的网关接口或者CGI,就是一个标准的集合.它定义信息怎样再问吧server和一般脚本间的交换. CGI的说明书是由NCSA维护,NCSA定义CGI的范畴:一般的网关接口或者CGI是外部网关程序的一 ...