热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

IDENTITY属性使用小结

IDEN

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


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

    一. 创建IDENTITY列

      if OBJECT_ID('test','U') is not null
      drop table testGOcreate 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 testDBCC CHECKIDENT('test', RESEED, 1)DBCC CHECKIDENT('test', NORESEED)--TRUNCATE表后会自动重置IDENTITY列TRUNCATE TABLE testDBCC 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_idselect 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 TRANSACTIONSET QUOTED_IDENTIFIER ONSET ARITHABORT ONSET NUMERIC_ROUNDABORT OFFSET CONCAT_NULL_YIELDS_NULL ONSET ANSI_NULLS ONSET ANSI_PADDING ONSET ANSI_WARNINGS ONCOMMITBEGIN TRANSACTIONGOCREATE TABLE dbo.Tmp_t_id
                  (
                  id int NOT NULL IDENTITY (1, 1),
                  c1 char(1) NULL
                  ) ON [PRIMARY]GOALTER TABLE dbo.Tmp_t_id SET (LOCK_ESCALATION = TABLE)GOSET IDENTITY_INSERT dbo.Tmp_t_id ONGOIF 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)')GOSET IDENTITY_INSERT dbo.Tmp_t_id OFFGODROP TABLE dbo.t_idGOEXECUTE sp_rename N'dbo.Tmp_t_id', N't_id', 'OBJECT' GOCOMMIT

                  对表上已存在列删除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)GOIF 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 null
                      drop table t_id
                      GO
                      create table t_id(id int identity(1,1),c1 char(1))
                      insert into t_idselect 'a' union allselect 'b'select * from t_id
                      SELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')--在表上新增一个列,把IDENTITY列值复制过去alter table t_id add id_new intGOupdate t_id set id_new = id--删除原来的列,并重命名新增列alter table t_id drop column idexec sp_rename 't_id.id_new','id'select * from t_idSELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')

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

                        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_idselect 1,'a' union allselect 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 idexec sp_rename 't_id.id_new','id'select * from t_idSELECT COLUMNPROPERTY(OBJECT_ID('t_id'),'id','IsIdentity')

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

                          br

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

                            set IDENTITY_INSERT test on;i
                            nsert 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 test2select 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 bon 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 bon 1=2

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

                                      if OBJECT_ID('test5','U') is not null
                                      drop table test5
                                      GO
                                      create table test5(id int identity, c1 char(1))
                                      select * from test5GOset 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 test5select 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 ALLSELECT 'Bar' UNION ALLSELECT '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 Temp2SELECT * 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/seusoftware/p/3804333.html#top

                                          文章经作者授权转载,版权归原文作者所有

                                          图片来源于网络,侵权必删!



                                          推荐阅读
                                          • 本文详细介绍了在 SQL Server 2005 中优化和实现分页存储过程的方法。通过创建一个名为 `[dbo].[GetUsers]` 的存储过程,该过程接受两个参数:`@RowIndex`(当前指定的页数)和 `@RecordCount`(每页显示的记录数)。文章不仅提供了具体的代码示例,还深入探讨了性能优化技巧,包括索引使用和查询优化策略,以提高分页查询的效率和响应速度。 ... [详细]
                                          • 如何在Java中使用DButils类
                                            这期内容当中小编将会给大家带来有关如何在Java中使用DButils类,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。D ... [详细]
                                          • 本文详细介绍了MySQL数据库的基础语法与核心操作,涵盖从基础概念到具体应用的多个方面。首先,文章从基础知识入手,逐步深入到创建和修改数据表的操作。接着,详细讲解了如何进行数据的插入、更新与删除。在查询部分,不仅介绍了DISTINCT和LIMIT的使用方法,还探讨了排序、过滤和通配符的应用。此外,文章还涵盖了计算字段以及多种函数的使用,包括文本处理、日期和时间处理及数值处理等。通过这些内容,读者可以全面掌握MySQL数据库的核心操作技巧。 ... [详细]
                                          • MySQL的查询执行流程涉及多个关键组件,包括连接器、查询缓存、分析器和优化器。在服务层,连接器负责建立与客户端的连接,查询缓存用于存储和检索常用查询结果,以提高性能。分析器则解析SQL语句,生成语法树,而优化器负责选择最优的查询执行计划。这一流程确保了MySQL能够高效地处理各种复杂的查询请求。 ... [详细]
                                          • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
                                            本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
                                          • 在Django中提交表单时遇到值错误问题如何解决?
                                            在Django项目中,当用户提交包含多个选择目标的表单时,可能会遇到值错误问题。本文将探讨如何通过优化表单处理逻辑和验证机制来有效解决这一问题,确保表单数据的准确性和完整性。 ... [详细]
                                          • 在 Kubernetes 中,Pod 的调度通常由集群的自动调度策略决定,这些策略主要关注资源充足性和负载均衡。然而,在某些场景下,用户可能需要更精细地控制 Pod 的调度行为,例如将特定的服务(如 GitLab)部署到特定节点上,以提高性能或满足特定需求。本文深入解析了 Kubernetes 的亲和性调度机制,并探讨了多种优化策略,帮助用户实现更高效、更灵活的资源管理。 ... [详细]
                                          • 在多线程并发环境中,普通变量的操作往往是线程不安全的。本文通过一个简单的例子,展示了如何使用 AtomicInteger 类及其核心的 CAS 无锁算法来保证线程安全。 ... [详细]
                                          • php更新数据库字段的函数是,php更新数据库字段的函数是 ... [详细]
                                          • 本文将详细介绍如何在Mac上安装Jupyter Notebook,并提供一些常见的问题解决方法。通过这些步骤,您将能够顺利地在Mac上运行Jupyter Notebook。 ... [详细]
                                          • 优化后的标题:Apache Cassandra数据写入操作详解
                                            本文详细解析了 Apache Cassandra 中的数据写入操作,重点介绍了 INSERT 命令的使用方法。该命令主要用于将数据插入到指定表的列中,其基本语法为 `INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...)`。通过具体的示例和应用场景,文章深入探讨了如何高效地执行数据写入操作,以提升系统的性能和可靠性。 ... [详细]
                                          • 在使用达梦数据库时,管理员可能会遇到连接频繁中断或特定SQL语句语法错误的问题。这些问题通常源于开发人员在创建对象时的不规范操作。为了解决这些问题,建议对数据库配置进行优化,并确保所有SQL语句符合达梦数据库的标准语法。此外,定期检查和维护数据库连接参数,以及对异常日志进行详细分析,也有助于及时发现并解决问题。 ... [详细]
                                          • 在使用 Cacti 进行监控时,发现已运行的转码机未产生流量,导致 Cacti 监控界面显示该转码机处于宕机状态。进一步检查 Cacti 日志,发现数据库中存在 SQL 查询失败的问题,错误代码为 145。此问题可能是由于数据库表损坏或索引失效所致,建议对相关表进行修复操作以恢复监控功能。 ... [详细]
                                          • 本文深入探讨了NoSQL数据库的四大主要类型:键值对存储、文档存储、列式存储和图数据库。NoSQL(Not Only SQL)是指一系列非关系型数据库系统,它们不依赖于固定模式的数据存储方式,能够灵活处理大规模、高并发的数据需求。键值对存储适用于简单的数据结构;文档存储支持复杂的数据对象;列式存储优化了大数据量的读写性能;而图数据库则擅长处理复杂的关系网络。每种类型的NoSQL数据库都有其独特的优势和应用场景,本文将详细分析它们的特点及应用实例。 ... [详细]
                                          • 在Linux系统中,网络配置是至关重要的任务之一。本文详细解析了Firewalld和Netfilter机制,并探讨了iptables的应用。通过使用`ip addr show`命令来查看网卡IP地址(需要安装`iproute`包),当网卡未分配IP地址或处于关闭状态时,可以通过`ip link set`命令进行配置和激活。此外,文章还介绍了如何利用Firewalld和iptables实现网络流量控制和安全策略管理,为系统管理员提供了实用的操作指南。 ... [详细]
                                          author-avatar
                                          子幽
                                          这个家伙很懒,什么也没留下!
                                          PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
                                          Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有