10. IDENTITY属性使用小结
从SQL Server 2012开始有了Sequence,简单用列如下:
CREATE SEQUENCE TestSeq START WITH 1 INCREMENT BY 1 ;SELECT NEXT VALUE FOR TestSeq AS NextValue;
在这之前,表中生成序列号大多都是借助IDENTITY列属性,当然也有一些时候,是在自定义表中,自己维护序列号。
一. 创建IDENTITY列
if OBJECT_ID('test','U') is not nulldrop table test GO create table test(id int identity, c1 char(1)) insert test values('a'); insert test values('b'); select * from test
1. 没有指定IDENTITY(seed ,increment),默认就是 IDENTITY(1, 1),效果同如下语句
create table test(id int identity(1,1), c1 char(1))
2. 通过函数或者系统视图,都可以查看是否为IDENTITY列
SELECT COLUMNPROPERTY(OBJECT_ID('test'),'id','IsIdentity') AS is_identityselect object_name(object_id) as table_name, is_identity,* from sys.columns where object_id=object_id('test') --and is_identity=1
3. 重置IDENTITY列的初始值,通常在数据删除/归档后进行
DELETE test DBCC CHECKIDENT('test', RESEED, 1) DBCC CHECKIDENT('test', NORESEED)--TRUNCATE表后会自动重置IDENTITY列 TRUNCATE TABLE test 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列值,就不会有问题了:
insert test values('z'); select SCOPE_IDENTITY() as curr_value
一个GO语句/批处理,也是一个上下文的分界点,但是SQL语句是顺序执行的,所以一个会话里,只要在INSERT之后用SCOPE_IDENTITY()来获取IDENTITY值是没问题的。
三. 修改IDENTITY列值/属性
1. 对已存在的列增加/删除IDENTITY属性
if OBJECT_ID('t_id') is not null drop table t_id GO create table t_id(id int,c1 char(1))insert into t_id select 1,'a' union all select 2,'b'alter table t_id alter column id int identity(1,2) /* Msg 156, Level 15, State 1, Line 2 Incorrect syntax near the keyword 'identity'. */
直接修改列属性会报错,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属性,生成的脚本如下:
BEGIN TRANSACTION SET QUOTED_IDENTIFIER ON SET ARITHABORT ON SET NUMERIC_ROUNDABORT OFF SET CONCAT_NULL_YIELDS_NULL ON SET ANSI_NULLS ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON COMMIT BEGIN TRANSACTION GO CREATE TABLE dbo.Tmp_t_id(id int NOT NULL IDENTITY (1, 1),c1 char(1) NULL) ON [PRIMARY] GO ALTER TABLE dbo.Tmp_t_id SET (LOCK_ESCALATION = TABLE) GO SET IDENTITY_INSERT dbo.Tmp_t_id ON GO IF EXISTS(SELECT * FROM dbo.t_id)EXEC('INSERT INTO dbo.Tmp_t_id (id, c1)SELECT id, c1 FROM dbo.t_id WITH (HOLDLOCK TABLOCKX)') GO SET IDENTITY_INSERT dbo.Tmp_t_id OFF GO DROP TABLE dbo.t_id GO EXECUTE sp_rename N'dbo.Tmp_t_id', N't_id', 'OBJECT' GO COMMIT
对表上已存在列删除IDENTITY属性,生成的脚本如下:
BEGIN TRANSACTION SET QUOTED_IDENTIFIER ON SET ARITHABORT ON SET NUMERIC_ROUNDABORT OFF SET CONCAT_NULL_YIELDS_NULL ON SET ANSI_NULLS ON SET ANSI_PADDING ON SET ANSI_WARNINGS ON COMMIT BEGIN TRANSACTION GO CREATE TABLE dbo.Tmp_t_id(id int NOT NULL,c1 char(1) NULL) ON [PRIMARY] GO ALTER TABLE dbo.Tmp_t_id SET (LOCK_ESCALATION = TABLE) GO IF EXISTS(SELECT * FROM dbo.t_id)EXEC('INSERT INTO dbo.Tmp_t_id (id, c1)SELECT id, c1 FROM dbo.t_id WITH (HOLDLOCK TABLOCKX)') GO DROP TABLE dbo.t_id GO EXECUTE sp_rename N'dbo.Tmp_t_id', N't_id', 'OBJECT' GO COMMIT
(2) 利用中间列
对表上已存在列删除IDENTITY属性
if OBJECT_ID('t_id') is not nulldrop table t_id GO create table t_id(id int identity(1,1),c1 char(1))insert into t_id select 'a' union all select 'b' select * from t_id SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')--在表上新增一个列,把IDENTITY列值复制过去 alter table t_id add id_new int GO update t_id set id_new = id--删除原来的列,并重命名新增列 alter table t_id drop column id exec sp_rename 't_id.id_new','id' select * from t_id SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')
对表上已存在列添加IDENTITY属性,用中间列的方式不太可行,因为IDENTITY列不接受UPDATE,新增的IDENTITY列无法直接复制原id的值,还得借助中间表,但如果不需要原来id的值,那么可以:
if OBJECT_ID('t_id') is not nulldrop table t_id GO create table t_id(id int,c1 char(1))insert into t_id select 1,'a' union all select 3,'b' select * from t_id SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')--在表上新增一个IDENTITY列,不复制原来的ID值 alter table t_id add id_new int identity(1,1) not null --删除原来的列,并重命名新增列 alter table t_id drop column id exec sp_rename 't_id.id_new','id' select * from t_id SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')
2. 在IDENTITY列上做增删改操作(DML)
(1) 删除操作没有问题,直接DELETE即可
delete test where id = 2
(2) 如果要显式INSERT某个值,需要开启IDENTITY_INSERT这个SESSION级的选项
set IDENTITY_INSERT test on; insert test(id,c1) values(3,'c'); set IDENTITY_INSERT test off; select * from test
(3) 如果要UPDATE IDENTITY列值,无论是否开启IDENTITY_INSERT这个选项都无法更新
set IDENTITY_INSERT test on; update test set id = 10 where id = 1 set IDENTITY_INSERT test off; /* Msg 8102, Level 16, State 1, Line 1 Cannot update identity column 'id'. */
非要修改的话,就得借助中间表,在不含IDENTITY属性的中间表里做完UPDATE,然后再把数据导回来。中间表可参考上面的脚本。
3. IDENTITY列属性复制
(1) 直接从单表SELECT INTO table_name,原表其他约束,索引等等都不会被复制,但是IDENTITY属性会被复制。
select * into test2 from test select * from test2 select columnproperty(OBJECT_ID('test'),'id','IsIdentity') select columnproperty(OBJECT_ID('test2'),'id','IsIdentity')
(2) 如果有IDENTITY属性的表和其他表JOIN,那么IDENTITY属性不会被复制。
select a.* into test3 from test a inner join sys.objects b on a.id = b.object_idselect * from test3 select columnproperty(OBJECT_ID('test3'),'id','IsIdentity')
假如复制表时,不想要IDENTITY属性,正好可以利用一下这个特点,如下:
select a.* into test4 from test a inner join sys.objects b on 1=2
(3) 如果用SELECT INTO table_name导数据时,FROM子句有多表关联,且想要保留IDENTITY属性,这时可以用INSERT,并考虑使用TABLOCK提示
if OBJECT_ID('test5','U') is not null drop table test5 GOcreate table test5(id int identity, c1 char(1)) select * from test5 GOset IDENTITY_INSERT test5 on; insert into test5 WITH(TABLOCK) (id,c1) select a.* from test a inner join test2 b on a.id = b.id set IDENTITY_INSERT test5 off;select * from test5 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以实现);
CREATE TABLE Temp1 ( ID INT IDENTITY(1,1) PRIMARY KEY, X VARCHAR(10) )INSERT INTO Temp1 OUTPUT INSERTED.* SELECT 'Foo' UNION ALL SELECT 'Bar' UNION ALL SELECT 'Baz'CREATE TABLE Temp2 ( ID INT PRIMARY KEY, X VARCHAR(10) )ALTER TABLE Temp1 SWITCH TO Temp2; SELECT COLUMNPROPERTY(OBJECT_ID('Temp1'),'id','IsIdentity') SELECT COLUMNPROPERTY(OBJECT_ID('Temp2'),'id','IsIdentity')INSERT INTO Temp2 OUTPUT INSERTED.* SELECT 10,'Foo' UNION ALL SELECT 20,'Bar' UNION ALL SELECT 5, 'Baz'UPDATE Temp2 SET ID = ID + 1;ALTER TABLE Temp2 SWITCH TO Temp1; SELECT * FROM Temp2 SELECT * FROM Temp1
另外,从SQL Server 2012开始,如果开发时使用了SEQUENCE,这些IDENTITY列的限制就都不会存在了。
四. IDENTITY函数
这是一个函数,使用时和IDENTITY属性的格式很相似,不过两者没什么关系,纯粹因为名字相同,顺便提一下。
select IDENTITY(int,1,1) as id into #t from sysobjectsselect cast(IDENTITY(int,1,1) as varchar(1000)) as id into #t2 from sysobjects -- can not use expression with identity function directly
IDENTITY函数限制比较多,只能用在SELECT INTO语句里,不能结合表达式使用,而且有了ROW_NUMBER(),IDENTITY函数就更显得不好用了。
10. IDENTITY属性使用小结相关推荐
- sqlserver IDENTITY属性使用小结
原文参考自 https://www.cnblogs.com/seusoftware/p/3804333.html 有删改,补充了一部分原文没看懂的. 从SQL Server 2012开始有了Sequ ...
- IDENTITY属性的使用详解
IDENTITY属性的使用 1.创建查看IDENTITY 创建 IF OBJECT_ID('test','U') IS NOT NULLDROP TABLE testGOCREATE TABLE te ...
- 纪中国庆10.5做题小结
纪中国庆10.5做题小结 T1:教主的花园 T2:教主泡嫦娥 T3:保镖排队 T4:教主的别墅 T1:教主的花园 Description [问题背景] LHX教主最近总困扰于前来膜拜他的人太多了,所以 ...
- pageInfo类属性含义小结
pageInfo类属性含义小结 pageInfo类可用于分页操作.以下为属性含义: private static final long serialVersionUID = 1L;private in ...
- sqlServer2008 自增列不能直接修改,必须将原有staff_no列删除,然后重新添加一列具有identity属性的
CREATE TABLE a_gztz_staff --员工表 ( staff_no int identity(1,1), --员工编号 自动递增 a_gzt_staff_pk varchar(120 ...
- flex弹性布局教程-10容器属性justify-content
本节目标 掌握justify-content的基础使用. 掌握项目多列排列使用justify-content对齐的技巧. 内容摘要 本篇介绍了容器属性justify-content,用来设置项目在主轴 ...
- 10. maven属性
1. maven属性 1.1 maven属性概述 经常在maven项目的pom文件中,看到这样的代码 <properties><spring.framework>4.0.4.R ...
- 夺命雷公狗ThinkPHP项目之----商城10商品属性管理
我们一般做项目前就要分析业务逻辑先,这次也不例外. attr_type:是指属性的类型,有唯一,单选和多选之分 唯一属性,是指用户在购买商品时,可以看到的扩展属性如下图所示: 单选属性,是指用户在购买 ...
- IDENTITY属性
当在表中心插入一行时,SQL Server会根据表中当前的标识值和增量生成一个新的标识值.如果需要获得这个新生成的标识值,可以查询:@@identity和SCOPE_IDENTITY(). @@ide ...
最新文章
- 技术图文:进一步完善自动化交易系统 - 01
- [待总结]高频率vim命令
- how to add one row in the dataframe?
- 基于DM642的X264开源代码实现的研究
- php关注 取消关注事件,微信公众平台开发关注/取消关注事件例子
- shell 中浮点数和整数的比较
- linux 下搭建subversion (svn),CentOS Linux 搭建 SVN(CollabNet Subversion)服务器,易用性和对远程团队的...
- Unix网络编程 chart
- R2: 相关系数、复相关系数及半偏相关系数之间的联系
- caffe的caffe.proto
- Linux chapter 7
- win7开机有画面进系统黑屏怎么办
- linux开发工程师前景_Linux就业前景如何?老男孩Linux入门
- 贪吃蛇c语言源代码彩色版,贪吃蛇C语言源代码
- 寻找肇事汽车车牌号C语言,北京交通大学C语言综合程序的设计(黄宇班).doc
- html5在线拍照,h5调用html5拍照上传功能
- Zigbee协议网络相关的东西(1)
- Win11打印机脱机了怎么重新连接?Win11打印机脱机的解决方法
- 在网络中狂奔:KCP协议
- Failed to determine a suitable driver class
热门文章
- 惠普Teradici PCoIP 受OpenSSL 漏洞影响,波及1500万个端点
- PyPI 管理员删除已潜伏10个月且下载量超1万次的恶意包
- 别问,问就是我差点在所有浏览器中注入 JavaScript 代码
- MySQL主主+Keepalived高可用(一):解决单点故障
- Skill Level 4 D23
- 如何编译 opencv3 和 opencv_contrib(Linux)
- BZOJ 3261: 最大异或和 [可持久化Trie]
- nodejs+express 初学(一)
- struts2中的session使用
- 2010-2014总结 ____V_V____ hello-world