【问题标题】:scope_identity vs ident_currentscope_identity 与 ident_current
【发布时间】:2009-02-19 21:08:30
【问题描述】:

经过大量研究,我对应该在 sql 中使用哪个身份跟踪器感到有些困惑。

据我了解,scope_identity 将为我提供从任何表更新的最后一个 id,而 ident_current 将返回指定表中的最后一个 id。

因此,鉴于这些信息,在我看来,最好使用的版本(如果您知道要更新哪个表)是 ident_current。然而,在阅读后,似乎大多数人更喜欢使用 scope_identity。这背后的原因是什么,我的逻辑是否存在缺陷?

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:

    在这种情况下你需要写表名,如果你决定改变表名会发生什么?然后,您也不能忘记更新您的代码以反映这一点。我总是使用 SCOPE_IDENTITY 除非我需要触发器中发生的插入 ID,然后我将使用 @@IDENTITY

    另外更大的区别是 IDENT_CURRENT 会给你来自另一个执行插入的进程的身份(换句话说,最后从任何用户生成的身份值) 因此,如果您进行插入,然后有人在您执行 SELECT IDENT_CURRENT 之前进行插入,您将获得该其他人的身份值

    另请参阅6 Different Ways To Get The Current Identity Value,其中有一些代码解释了当您将触发器放在桌面上时会发生什么

    【讨论】:

      【解决方案2】:

      从我读到的范围_identity() 应该是正确的答案,但是如果您的插入结果看起来像 SQL 2005 和 SQL 2008 中的一个错误可以发挥作用并行查询计划。

      查看以下文章了解更多详情:

      @@IDENTITY vs SCOPE_IDENTITY() vs IDENT_CURRENT - Retrieve Last Inserted Identity of Record

      文章:Six reasons you should be nervous about parallelism

      请参阅标题为:1 的部分。 #328811, "SCOPE_IDENTITY() 有时会返回不正确的值"

      【讨论】:

      【解决方案3】:

      请参阅this 博文以获取详细答案。由于触发器完成了插入,Scope_identity 将永远不会返回身份。在表名发生变化的变化世界中使用 ident_current 并不是一个好主意。就像在开发环境中一样。

      【讨论】:

      • ident_current 永远不应该用于获取您刚刚插入的值,如果您使用它,它会给您带来数据完整性问题,因为即使它不是来自您的连接,它也会给出最后插入的身份!如果您在数据库中的任何地方都使用过此代码,那么您将面临严重的数据错误风险。
      • 我有说过任何地方都应该使用 ident_current 吗?
      【解决方案4】:
      /*
      * IDENT_CURRENT returns the last identity value generated for a specific table in any session and any scope.
      * @@IDENTITY returns the last identity value generated for any table in the current session, across all scopes.
      * SCOPE_IDENTITY returns the last identity value generated for any table in the current session and the current scope.
      */
      
      IF OBJECT_ID(N't6', N'U') IS NOT NULL
          DROP TABLE t6 ;
      GO
      IF OBJECT_ID(N't7', N'U') IS NOT NULL
          DROP TABLE t7 ;
      GO
      CREATE TABLE t6 (id INT IDENTITY) ;
      CREATE TABLE t7
          (
           id INT IDENTITY(100, 1)
          ) ;
      GO
      CREATE TRIGGER t6ins ON t6
          FOR INSERT
      AS
          BEGIN
              INSERT  t7
                      DEFAULT VALUES
          END ;
      GO
      --End of trigger definition
      
      SELECT  id
      FROM    t6 ;
      --IDs empty.
      
      SELECT  id
      FROM    t7 ;
      --ID is empty.
      
      --Do the following in Session 1
      INSERT  t6
              DEFAULT VALUES ;
      SELECT  @@IDENTITY ;
      /*Returns the value 100. This was inserted by the trigger.*/
      
      SELECT  SCOPE_IDENTITY() ;
      /* Returns the value 1. This was inserted by the
      INSERT statement two statements before this query.*/
      
      SELECT  IDENT_CURRENT('t7') ;
      /* Returns 100, the value inserted into t7, that is in the trigger.*/
      
      SELECT  IDENT_CURRENT('t6') ;
      /* Returns 1, the value inserted into t6 four statements before this query.*/
      
      -- Do the following in Session 2.
      SELECT  @@IDENTITY ;
      /* Returns NULL because there has been no INSERT action
      up to this point in this session.*/
      
      SELECT  SCOPE_IDENTITY() ;
      /* Returns NULL because there has been no INSERT action
      up to this point in this scope in this session.*/
      
      SELECT  IDENT_CURRENT('t7') ;
      /* Returns 100, the last value inserted into t7.*/
      

      【讨论】:

        【解决方案5】:

        理论说:要注意竞争条件并且不关心触发器内的插入,您应该使用SCOPE_IDENTITY()但是...... SCOPE_IDENTITY() 上有已知的错误(和@@IDENTITY),如在其他回答者上提到并链接。 Here are the workarounds from Microsoft 考虑到了这个错误。

        在文章中最相关的部分下方。它使用output插入的子句:

        DECLARE @MyNewIdentityValues table(myidvalues int)
        declare @A table (ID int primary key)
        insert into @A values (1)
        declare @B table (ID int primary key identity(1,1), B int not null)
        insert into @B values (1)
        select
            [RowCount] = @@RowCount,
            [@@IDENTITY] = @@IDENTITY,
            [SCOPE_IDENTITY] = SCOPE_IDENTITY()
        
        set statistics profile on
        insert into _ddr_T
        output inserted.ID into @MyNewIdentityValues
            select
                    b.ID
                from @A a
                    left join @B b on b.ID = 1
                    left join @B b2 on b2.B = -1
        
                    left join _ddr_T t on t.T = -1
        
                where not exists (select * from _ddr_T t2 where t2.ID = -1)
        set statistics profile off
        
        select
            [RowCount] = @@RowCount,
            [@@IDENTITY] = @@IDENTITY,
            [SCOPE_IDENTITY] = SCOPE_IDENTITY(),
            [IDENT_CURRENT] = IDENT_CURRENT('_ddr_T')
        select * from @MyNewIdentityValues
        go
        

        【讨论】:

          【解决方案6】:

          SELECT IDENT_CURRENT——正如你所说,将为你提供特定于表的最后插入的标识值。存在与此相关的问题,一是用户需要有权查看元数据,否则它会返回 NULL,二是您正在硬编码表的名称,如果表名称发生更改,这将导致问题。

          最佳实践是将 Scope_Identity 与变量一起使用...看下面的示例

           DECLARE @myFirstTableID INT
          
            DECLARE @mySecondTableID INT
          
            INSERT INTO MYFirstTable (....) VALUES (.....)
          
             SELECT @myFirstTableID =SCOPE_IDENTITY()
          
          
            INSERT INTO MYSecondTable () VALUES (.....)
          
          
             SELECT @mySecondTableID=SCOPE_IDENTITY()
          

          因此,通过在感兴趣的插入语句旁边使用 variable 和 scope_identity,您可以确保从正确的表中获取正确的标识。 享受

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-12-27
            • 1970-01-01
            • 1970-01-01
            • 2017-04-11
            • 1970-01-01
            • 1970-01-01
            • 2013-08-18
            相关资源
            最近更新 更多