原文参考自 https://www.cnblogs.com/seusoftware/p/3804333.html  有删改,补充了一部分原文没看懂的。

从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 --delete后不会清除identity取值
DBCC CHECKIDENT('test', RESEED, 0) --RESEED会+1,所以给0,才会从1开始
DBCC CHECKIDENT('test', NORESEED) --查看当前identity取值,和表中的最大identity值
--Checking identity information: current identity value '2', current column value '3'. select * from test /* id c1 2 a 3 b 1 a 2 b */
TRUNCATE TABLE test --trucnate会清除identity取值
DBCC CHECKIDENT('test', NORESEED)
--Checking identity information: current identity value 'NULL', current column value 'NULL'.

二、 获取IDENTITY列值

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

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

获取IDENTITY列值有三种方式:

  • (1) IDENT_CURRENT( 'table_name' ) 返回所有会话和作用域中特定表最后生成的标识值。针对特定表,是全局的。
  • (2) @@IDENTITY 返回当前会话的所有作用域中所有表最后生成的标识值。
  • (3) SCOPE_IDENTITY() 返回当前会话和作用域中所有表最后生成的标识值。推荐的,安全的方法。

举个例子

比如,我有表 A 和 B,都有IDENTITY自增列,在表 A 上定义了一个Insert触发器,当在表 A 中插入一条数据时,自动在表 B 也插入一条数据。此时,大家注意,有两个原子操作:在A中插入一条数据, 接着在B中随后插入一条数据。

那么我们在表 A 插入一条数据后,使用SELECT @@IDENTITY 输出时,输出的到底是 A 还是 B 的自增域的值呢?答案很明显,最后插入就输出谁,那么就是 B 了(因为可以跨作用域)。于是,我本意是想得到 A 的自增域值,结果得到了 B 的自增域值,一只 BUG 随之诞生,搞不好还会影响到整个系统数据的混乱。

三、 修改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级的选项

--注意表是否在当前数据库
use testing_test
go
if OBJECT_ID('test_insert','U') is not nulldrop table test_insert
create table test_insert(id int identity(1,1), c1 int)use master
SET IDENTITY_INSERT test_insert ON
/*
Msg 1088, Level 16, State 11, Line 2
Cannot find the object "test_insert" because it does not exist or you do not have permissions.
*/insert into testing_test.dbo.test_insert(c1) values(100)
select * from testing_test.dbo.test_insert
/*
id    c1
1    100
*/SET IDENTITY_INSERT testing_test..test_insert ON  --可以跨数据库指定表名
insert into testing_test.dbo.test_insert(id,c1) values(100,100)
select * from testing_test.dbo.test_insert
/*
id    c1
1    100
100    100
*/SET IDENTITY_INSERT testing_test..test_insert OFF
insert into testing_test.dbo.test_insert(c1) values(100) --开启IDENTITY_INSERT后会reseed identity值
select * from testing_test.dbo.test_insert
/*
id    c1
1    100
100    100
101    100
*/

(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函数就更显得不好用了。

参考

https://www.cnblogs.com/raincedar/p/8509414.html

sqlserver IDENTITY属性使用小结相关推荐

  1. 10. IDENTITY属性使用小结

    原文:10. IDENTITY属性使用小结 从SQL Server 2012开始有了Sequence,简单用列如下: CREATE SEQUENCE TestSeq START WITH 1 INCR ...

  2. pageInfo类属性含义小结

    pageInfo类属性含义小结 pageInfo类可用于分页操作.以下为属性含义: private static final long serialVersionUID = 1L;private in ...

  3. sqlServer2008 自增列不能直接修改,必须将原有staff_no列删除,然后重新添加一列具有identity属性的

    CREATE TABLE a_gztz_staff --员工表 ( staff_no int identity(1,1), --员工编号 自动递增 a_gzt_staff_pk varchar(120 ...

  4. IDENTITY属性的使用详解

    IDENTITY属性的使用 1.创建查看IDENTITY 创建 IF OBJECT_ID('test','U') IS NOT NULLDROP TABLE testGOCREATE TABLE te ...

  5. IDENTITY属性

    当在表中心插入一行时,SQL Server会根据表中当前的标识值和增量生成一个新的标识值.如果需要获得这个新生成的标识值,可以查询:@@identity和SCOPE_IDENTITY(). @@ide ...

  6. Raphael属性方法小结一

    关于Raphael的属性,总结了一张xmd图片,如下点击打开链接

  7. sqlserver CONVERT()函数用法小结

    CONVERT的使用方法: 格式: CONVERT(data_type,expression[,style]) 说明: 此样式一般在时间类型(datetime,smalldatetime)与字符串类型 ...

  8. Sql Server插入数据并返回自增ID,@@IDENTITY,SCOPE_IDENTITY和IDENT_CURRENT的区别

    预备知识:SQLServer的IDENTITY关键字 IDENTITY关键字代表的是一个函数,而不是identity属性.在access里边没有这个函数,所以在access不能用这个语句.语法:ide ...

  9. SQLServer DBA 三十问

    原贴:http://www.cnblogs.com/fygh/archive/2011/10/18/2216166.html 答案:https://blog.csdn.net/cjssimei527/ ...

最新文章

  1. getURLParameters - 网址参数
  2. qt5.13.2输出中文乱码
  3. mysql 视图 mybatis_Mybatis调用视图和存储过程的方法
  4. Android 系统性能优化(27)---内存分析工具
  5. 国产特斯拉近两月出口5.5万辆 今年已出口近10万辆
  6. 【JavaScript】支持js代码的博客有…
  7. 计算机系统盘怎么扩充,如何给电脑c盘扩容
  8. HTML:利用canvas画定位图标
  9. 动词ing形式的5种用法_动词 ing 形式用法归纳
  10. 【面经——虎牙实习+一面+HR面+offer】
  11. JavaScript Web APIs部分参考pink老师ppt(网页常见的js案例)
  12. 警察规范执法案例_警察改革沉浸式技术可以改变执法方式
  13. arcgis 投影坐标系的区分
  14. 浅谈xhr和fetch
  15. java程序员培训学院,年薪60W必备
  16. 运用Java制作一个属于自己的音乐播放软件
  17. 解决:RecyclerView只显示一行数据
  18. 英语每日阅读---2、越来越多人反对人工智能参战
  19. 实验室安全 考试 题库
  20. Spring技术_邮箱注册_激活_获取验证码

热门文章

  1. 接口测试与调试工具-HTTPie
  2. sdk - 软件开发工具包
  3. nginx静态资源无法访问问题(nginx入口搞错问题)
  4. 【线性代数】3-2:零空间(Nullspace)
  5. appium 安装方式
  6. TP5 model 模型事件
  7. 使用DSFD检测DarkFace数据集过程
  8. 抢红米有感 :雷军的营销智慧
  9. samba服务器传输大量文件,smbclient操作命令,服务器文件传输
  10. 讲讲Linux系统工程师的职业规划