`
zhangziyueup
  • 浏览: 1171063 次
文章分类
社区版块
存档分类
最新评论

Sql Server中如何准确获得标识值

 
阅读更多

SQL Server有三种不同的函数可以用来获得含有标识列的表里最后生成的标识值:

  •   @@IDENTITY
  •   SCOPE_IDENTITY()
  •   IDENT_CURRENT('数据表名')

  以上三个函数虽然都可以返回数据库引擎最后生成插入标识列的值,但是根据插入行的来源(例如:存储过程或触发器)以及插入该行的连接不同,这三个函数在功能上也有所不同。

  @@IDENTITY函数可以返回所有范围内当前连接插入最后所生成的标识值(包括任何调用的存储过程和触发器)。这个函数不止可以适用于表。函数返回的值是最后表插入行生成的标识值。

  SCOPE_IDENTITY()函数跟上一个函数几乎是一摸一样的,不同的地方:即前者返回的值只限于当前范围(即执行中的存储过程)。

  最后是IDENT_CURRENT函数,它可以用于所有范围和所有连接,获得最后生成的表标识值。跟前面两个函数不同的是,这个函数只用于表,并且使用[数据表名]作为一个参数。

我们可以举实例来演示上述函数是如何运作的。

  首先,我们创建两个简单的例表:一个代表客户表,一个代表审计表。创建审计表的目的是为了跟踪数据库里插入和删除信息的所有记录。

以下是引用片段:
  CREATETABLEdbo.customer
  (customeridINTIDENTITY(1,1)PRIMARYKEY)
  GO
  CREATETABLEdbo.auditlog
  (auditlogidINTIDENTITY(1,1)PRIMARYKEY,
  customeridINT,actionCHAR(1),
  changedatedatetimeDEFAULTGETDATE())
  GO

  然后,我们还要创建一个存储过程和一个辅助触发器,这个存储过程将在数据库表里插入新的客户行,并返回生成的标识值,而触发器则会向审计表插入行:

以下是引用片段:
  CREATEPROCEDUREdbo.p_InsertCustomer@customeridINToutput
  AS
  SETnocountON
  INSERTINTOdbo.customerDEFAULTVALUES
  SELECT@customerid=@@identity
  GO

  CREATETRIGGERdbo.tr_customer_logONdbo.customer
  FORINSERT,DELETE
  AS
  IFEXISTS(SELECT'x'FROMinserted)
  INSERTINTOdbo.auditlog(customerid,action)
  SELECTcustomerid,'I'
  FROMinserted
  ELSE
  IFEXISTS(SELECT'x'FROMdeleted)
  INSERTINTOdbo.auditlog(customerid,action)
  SELECTcustomerid,'D'
  FROMdeleted
  GO

  现在我们可以执行程序,创建客户表的第一行了:

以下是引用片段:
  DECLARE@customeridINT
  EXECdbo.p_InsertCustomer@customeridoutput
  SELECT@customeridAScustomerid

  执行后返回了我们需要的第一个客户的值,并记录了插入审计表的条目。到目前为止,数据显示没有任何问题。

  假设由于先前沟通出现了偏差,一个客户服务代表现在需要从数据库里删除掉这个新增的客户。我们现在就来把新插入的客户行删除掉:

以下是引用片段:
  DELETEFROMdbo.customerWHEREcustomerid=1

  现在,客户工作表为空表,而审计工作表里则有两行——第一行是记录第一次插入行,第二行是记录删除客户记录。

  现在我们再往数据库里增加第二个客户信息并检测一下获得的标识值:

以下是引用片段:
  DECLARE@customeridINT
  EXECdbo.p_InsertCustomer@customeridoutput
  SELECT@customeridAScustomerid

  哇!看看出现了什么情况!如果我们现在再看客户工作表,就会发现虽然创建了客户2,但是我们的程序返回的标识值为3!到底出了什么问题呢?回想一下,前面讲过@@IDENTITY函数的作用范围,它会返回主程序调用的任何存储过程或触动任何触发器最后生成的标识值,取决于哪一个在函数被调用前最后生成标识值。在我们的例子里,初始范围是p_InsertCustomer,然后是触发器用来记录插入条目的tr_customer_log。因此我们返回获得的标识值是审计工作表里触发器插入生成的标识值,而不是我们想要的客户工作表里的生成的标识值。

  在SQL Server 2000之前的版本,@@IDENTITY函数是获得标识值的唯一方法。由于会出现这样的存储过程/触发器问题,SQL Server开发团队在SQL Server 2000中引入了 SCOPE_IDENTITY()和IDENT_CURRENT这两个函数来解决这个问题。所以在旧的SQL Server版本里,要解决这个问题比较麻烦。如果是SQL Server6.5版本,我建议可以去掉标识列,然后创建一个可以包含下一个需要使用的值的辅助表,可以达到标识列的作用效果。不过这个办法也不是什么高明的办法。

  现在我们来修改一下存储过程来使用SCOPE_IDENTITY()函数,并重新执行程序来添加第三个客户条目:

以下是引用片段:
  ALTERPROCEDUREdbo.p_InsertCustomer@customeridINToutput
  AS
  SETnocountON
  INSERTINTOdbo.customerDEFAULTVALUES
  SELECT@customerid=SCOPE_IDENTITY()
  GO
  DECLARE@customeridINT
  EXECdbo.p_InsertCustomer@customeridoutput
  SELECT@customeridAScustomerid

  我们返回的标识值还是3,不过这次我们获得的标识值是正确的,因为我们添加了第三个客户条目。如果我们检查一下审计工作表,就会发现里面已经有第四个条目记录新插入的客户记录。由于函数SCOPE_IDENTITY()只作用于当前范围,只返回当前执行程序的值,这样就避免了发生刚才那样的问题。

  前面讲过,函数@@IDENTITY和函数SCOPE_IDENTITY()不止用于表,不像函数IDENT_CURRENT那样可以用表作为参数。使用@@IDENTITY和SCOPE_IDENTITY()这两个函数的话在设置代码时需要加倍小心,才能够从所需要的表里获得正确的标识值。从表面上来看,放弃这两个函数,只使用函数IDENT_CURRENT并指定表是更安全的办法。这样可以避免出现获得错误标识值的情况,对吧?记得先前说过函数IDENT_CURRENT不仅会跨范围,而且它还会跨连接。也就是说,使用这个函数生成的值不仅仅限于你的连接所执行的程序,它的涵盖范围还包括整个数据库所有的连接。因此,即使是在规模较小的OLTP环境里,它也会出现不能准确返回所需值的问题。这样就可能发生类似前面@@IDENTITY函数/触发器的数据损坏问题。

  我的建议是函数SCOPE_IDENTITY()是三个函数里最安全的函数,应该设置为默认函数。使用这个函数,你可以放心地添加触发器和次存储过程,无需担心意外损坏数据。而另外两个函数可以保留应付特殊的情况,当遇到需要使用这两个函数的特殊情况时,建议记录它们的使用情况并进行测试。

小技巧:

  • Sql Server判断表是存在标识列

If Exists(Select * from SysColumns Where ID=OBJECT_ID(N'TEST1') And COLUMNPROPERTY(ID,Name,'IsIdentity')=1)
Print N'有自增列'
Else
Print N'没有自增列'
  • Sql Server显示当前数据库包含自增列的表

Select b.name,a.* from SysColumns a,sysobjects b Where a.id=b.id and COLUMNPROPERTY(a.ID,a.Name,'IsIdentity')=1

分享到:
评论

相关推荐

    Microsoft SQL Server 2005 Express Edition SP3

    SQL Server Express 可从此 Microsoft 网站下载获得。 注意: 下载和提取产品时,请务必按照下载站点提供的说明进行操作。 安装 SQL Server Express 之后,请在命令提示符下使用以下命令连接到 SQL Server ...

    远程连接不上SQLSERVER

    为SQLSERVER的默认连接协议,你可以在CLIENT NETWORK UTILITY中看到TCP/IP和NAME PIPE 的顺序。 你也可以在: [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SuperSocketNetLib] "ProtocolOrder"=...

    sql server 集群配置

    分类: SQL Server  Windows server2003 + sql server2005 集群配置安装 一:环境 软硬件环境    虚拟3台windows server 2003主机。其中一台做域控DC,另外两台作为节点win1 win2. 域控DC网络设置: Hostname ...

    Log Explorer for SQL Server v4.22 含注册机

    他可以支持SQLServer7.0、SQLServer2000和SQLServer2005,提取标准数据库的日志文件或者备份文件中的信息。 其中提供两个强大的工具:日志分析浏览,对象恢复。具体功能如下: l 日志文件浏览 l 数据库变更审查 l...

    SQL Server数据库复习题

    1、根据关系数据基于的数据模型——关系模型的特征判断下列正确的一项:( ) A、只存在一对多的实体关系,以图形方式来...A、Windows身份验证 B、SQL Server 身份验证 C、以超级用户身份登录时 D、其他方式登录时

    Log Explorer for SQL Server v4.22

    他可以支持SQLServer7.0和SQLServer2000,提取标准数据库的日志文件或者备份文件中的信息。 其中提供两个强大的工具:日志分析浏览,对象恢复。具体功能如下: l 日志文件浏览 l 数据库变更审查 l 计划和授权变更...

    SQL Server从入门到精通(三)

    v表中一列或几列组合的,能够唯一的标识表中的每一行 √一个表只能有一个主键 √多列组合当主键称为复合主键其值自动被填入 原则:最少性和唯一性 外键: √相对应于主键而言 √一个表可以有多个外键 标识列: √...

    SQLServer数据库设计.pdf

    SQLServer数据库设计 数据库设计 ⼀、数据库设计的必要性 在实际的软件项⽬中,如果系统中需要存储的数据量⽐较⼤,需要设计的表⽐较多,表与表之间的关系⽐较复杂,那我们就需要进⾏规 范的数据库设置。如果不经过...

    迷你版SQL2000服务器

    +增加内存管理页,控制SQL Server内存占用,与企业管理器相同。 1.2.1 ^未开启服务时,关闭窗口会退出程序,开启服务时,关窗窗口则是隐藏; *附加数据库时,修正目标目录与源目录相同时无法附加的问题。 1.2.0 ...

    sql试 题答案和试题

    6、 在当SQL SERVER实例中有DBOA数据库,要将它设置成为单用户状态,请写出设置命令:___________________________________________。 7、 删除表命令是:___________________________________________。 8、 用...

    sql2000 Log Explorer4.2(含注册码)+汉化

    他可以支持SQLServer7.0和SQLServer2000,提取标准数据库的日志文件或者备份文件中的信息。 其中提供两个强大的工具:日志分析浏览,对象恢复。具体功能如下: l 日志文件浏览 l 数据库变更审查 l 计划和授权变更...

    sql文件编辑器,免费版

     28、*在没有安装SqlServer 客户端及任何组件的情况下仍然可以连接至SqlServer*  29、*判断Oracle的常见错误,并尝试更改或给出提示*  30、增加历史记录功能。  31、增加自动保存用户状态功能。再次打开软件...

    C#开发经验技巧宝典

    0873 如何正确理解SQL中的NULL值 516 0874 如何使用IsNull()函数来处理空值 516 0875 如何使用Nullif()函数来处理空值 516 0876 查询空值(NULL)的技巧 517 0877 利用关键字DISTINCT去除重复记录 517 ...

    迷你SQL2000

    +增加内存管理页,控制SQL Server内存占用,与企业管理器相同。 1.2.1 ^未开启服务时,关闭窗口会退出程序,开启服务时,关窗窗口则是隐藏; *附加数据库时,修正目标目录与源目录相同时无法附加的问题。 1.2.0 ...

    SQL数据库设计.doc

    最后决定选哪种数据库(Oracle、SQLServer、MySQL) 来建库、建表。 Ø 需求分析阶段:数据库系统分析 秀气分析阶段的重点是调查、收集、分析客户的业务数据需求以及数据的安全性、完整 性需求等。 需求分析步骤: 1...

    Oraclet中的触发器

    REFERENCING 子句说明相关名称,在行触发器的PL/SQL块和WHEN 子句中可以使用相关名称参照当前的新、旧列值,默认的相关名称分别为OLD和NEW。触发器的PL/SQL块中应用相关名称时,必须在它们之前加冒号(:),但在WHEN...

    SQL sever 实训

    SQL sever 2008 Rar! ?s X祕BwL0 17240671-1.sql ?年3月9日 --1.查询course表的所有信息(所有行所有列) USE Xk GO SELECT * FROM Course --有哪些种类的选修课?学分是多少 USE XK GO SELECT Kind,Credit FROM ...

    数据库资料

    SQL Server中存在五种约束,分别是:主键约束、外键约束、检查约束、默认约束和唯一性约束(唯一性约束将在后续课程中使用SQL语句实现)总结创建数据库表需要:确定表的列名、数据类型、是否允许为空,还需要确定...

    Oracle9i的init.ora参数中文说明

    说明: 如果值为TRUE, 即使源长度比目标长度 (SQL92 兼容) 更长, 也允许分配数据。 值范围: TRUE | FALSE 默认值: FALSE serializable: 说明: 确定查询是否获取表级的读取锁, 以防止在包含该查询的事务处理被提交...

Global site tag (gtag.js) - Google Analytics