以下是引用片段:
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
哇!看看出现了什么情况!如果我们现在再看客户工作表,就会发现虽然创建了客户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