【问题标题】:Retrieving auto-incremented column value from table where multiple insert/selects in single stored procedure从单个存储过程中的多个插入/选择的表中检索自动递增的列值
【发布时间】:2009-05-29 13:40:25
【问题描述】:

我有一个包含多个插入/选择语句的存储过程。假设我正在使用第一个插入来填充“经理”表。在插入时,会添加一个 ManagerId(自动递增),但不会在插入语句中引用。然后我希望使用该表中的 ManagerId 将一行插入另一个表,其中 ManagerId 是外键。示例代码如下..

USE [TEST]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[sptInsertNewManager]
    -- Add the parameters for the stored procedure here
    @FName varchar(50),
    @LName varchar(50),
    @EMail varchar(100),   
    @UserRoleID int,
    @LANUserID varchar(25), 
    @GroupID int
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for procedure here
INSERT INTO [Manager]           
          ([FName], 
           [LName],
           [Email], 
           [UserRoleID],
           [LANUserID],          
           [ActiveFlag],
           [GroupID]
)
     VALUES
            (@FName
            ,@LName
            ,@EMail
            ,@UserRoleID
            ,@LANUserID
            ,1 
            ,@GroupID);

COMMIT

SELECT ManagerId FROM [Manager] AS newManager WHERE LANUserID = @LANUserID; 
        --also insert into Users table.
    INSERT INTO [dbo].[aspnet_Users] (
        [UserId],
        [UserName],
        [LoweredUserName],
        [ManagerId]
    )
        VALUES (
            NEWID(),
            @LANUserID,
            LOWER(@LANUserID),
            newManager)
END

这显然行不通。这是我解决这个问题的尝试。我对 SQL 还很陌生,所以对于这个问题的任何帮助将不胜感激。

【问题讨论】:

    标签: sql sql-server stored-procedures


    【解决方案1】:

    在插入后使用 scope_identity() 从当前范围内捕获最新的单个标识值:

    DECLARE @ID     int
    
    
    INSERT ......
    SELECT @ID=scope_identity()
    

    在需要的地方使用@ID

    注意:SCOPE_IDENTITY() 比旧的 @@IDENTITY 更受欢迎,因为它给出了当前范围内的最后一个 Identity 值,这避免了插入日志表(带有标识)的触发器的问题。

    但是,如果您需要多个标识值(插入一组行),请使用 OUTPUT 和 INTO:

    declare @test table (RowID  int identity(1,1) primary key not null, RowValue varchar(10) null)
    declare @OutputTable table (RowID int not null)
    
    insert into @test (RowValue)
        OUTPUT INSERTED.RowID
        INTO @OutputTable
        SELECT 'A'
        UNION SELECT 'B'
        UNION SELECT 'C'
        UNION SELECT 'D'
        UNION SELECT 'E'
    
    
    select * from @OutputTable
    

    输出:

    (5 row(s) affected)
    RowID
    -----------
    1
    2
    3
    4
    5
    
    (5 row(s) affected)
    

    【讨论】:

      【解决方案2】:

      用于 MS Sql 服务器

      每当您插入一条记录并且它具有自动增量列(ms-sql 用语中的标识列)时,您都可以使用它来检索您插入的行的 id:

      @id = SCOPE_IDENTITY()
      

      这可确保您在代码运行期间(在插入和通过 scope_identity() 获取身份之间)获得插入生成的标识列值,而不是由其他用户生成的值)

      【讨论】:

        【解决方案3】:

        糟糕...你的 proc 和事务都搞砸了...

        您希望这两个插入发生在同一个事务中(我从原始过程中间的COMMIT 语句中推断出)。因此,您需要在所有 INSERTSELECT 语句周围适当地放置一个 BEGIN TRANSACTIONCOMMIT 语句,以隔离数据更改。

        考虑以下几点:

        CREATE PROCEDURE [dbo].[sptInsertNewManager]
            -- Add the parameters for the stored procedure here
            @FName varchar(50),
            @LName varchar(50),
            @EMail varchar(100),   
            @UserRoleID int,
            @LANUserID varchar(25), 
            @GroupID int
        AS
        BEGIN
            -- SET NOCOUNT ON added to prevent extra result sets from
            -- interfering with SELECT statements.
            SET NOCOUNT ON;
        
        DECLARE @ManagerID  INT
        
        BEGIN TRANSACTION    
        
            -- Insert statements for procedure here
        INSERT INTO [Manager]           
                  ([FName], 
                   [LName],
                   [Email],     
                   [UserRoleID],
                   [LANUserID],          
                   [ActiveFlag],
                   [GroupID]
        )
             VALUES
                        (@FName
                        ,@LName
                        ,@EMail
                    ,@UserRoleID
                    ,@LANUserID
                        ,1 
                    ,@GroupID);
        
        -- Collect the ID you just created
        SELECT @ManagerID = SCOPE_IDENTITY()
        
        
                --also insert into Users table.
            INSERT INTO [dbo].[aspnet_Users] (
                [UserId],
                [UserName],
                [LoweredUserName],
                [ManagerId]
            )
                VALUES (
                        NEWID(),
                        @LANUserID,
                        LOWER(@LANUserID),
                        @ManagerID)   -- This is the new identity you just created
        
        COMMIT
        
        END
        

        【讨论】:

          【解决方案4】:

          同样不要在事务中间提交,如果一个插入失败,您希望两个插入都回滚。阅读有关事务处理的在线书籍。

          【讨论】:

            【解决方案5】:

            我们可以像这样返回标识列:

            SELECT @@identity as 'Identity'
            

            并在如下代码中读取此标识值:

            int ID = Convert.ToInt32(cmdinsert.ExecuteScalar());
            

            【讨论】:

              【解决方案6】:

              您可以使用@@Identity 全局变量来检索最后插入的标识值

              SELECT @ManagerId=@@Identity
              

              您还可以使用 Scope_Identity 和 IDENT_Current 函数

              【讨论】:

              • @@dentity 会给你最后一个身份,即使它不是来自你范围内的插入(比如插入触发触发器)
              • 同意KM,我永远不会使用@@identity,因为迟早你会遇到数据完整性问题。与 ident_current 相同(您可以获得在您之后插入的另一个进程的身份)。如果我想要我刚刚插入的身份,我使用 scope_identity 或在较新的版本上,您可以使用输出子句(不确定这是在 2005 年还是 2008 年出现,因为我们跳过了 2005 年),它的优点是返回整个身份集如果您运行基于集合的多条记录插入,则为值。
              猜你喜欢
              • 2017-12-09
              • 1970-01-01
              • 1970-01-01
              • 2014-05-28
              • 2013-02-24
              • 1970-01-01
              • 1970-01-01
              • 2012-07-21
              • 1970-01-01
              相关资源
              最近更新 更多