4 sql-server identity sql-server-2012
我有一个用于在两个表中插入值的存储过程。
这些表具有父子关系,即第一个表具有标识列,第二个表引用第一个表。
在以下过程中,我将Scope_Identity()
值作为列返回:
ALTER PROCEDURE [dbo].[Insert_Header_Details_Tables]
(
@ProcessID [int]=null,
@FileName [varchar](50)=null,
@VendorName [varchar](250)=null,
@LastName [nvarchar](100)=null,
@FirstName [nvarchar](100)=null,
@isFirstMSH [bit]
)
AS
--BEGIN TRANSACTION
-- Insert into Jobs table
IF(@isFirstMSH = 1)
BEGIN
INSERT INTO LabStagingHeader
([FileName],
[VendorName]
)
VALUES (@FileName,
@VendorName
)
END
-- Retrieve the automatically @ProcID VALUE from the Header table
SET @ProcessID = SCOPE_IDENTITY()
-- Insert new values into LabStagingDetails table
INSERT INTO LabStagingDetails
([ProcessID],
[LastName],
[FirstName]
)
VALUES (@ProcessID,
@LastName,
@FirstName
)
Run Code Online (Sandbox Code Playgroud)
问题是当 isFirstMSH 为 false 时,ProcessID 值为 NULL。如果 isFirstMSH 值为 false,则应在表中插入最后生成的值。
如果您多次调用该过程,则它们是不同的scopes,因此SCOPE_IDENTITY()
预计为null
. 并且您需要注意概念 - 如果您多次调用该过程,第二次调用如何真正确保“最后生成的值”来自该进程的前一次调用,而不是其他一些并发调用同样的程序?
考虑表值参数,以便您只需调用一次存储过程,如果此调用插入“第一”行并担心多个用户发生冲突,您可以停止跟踪:
CREATE TYPE dbo.TVPLabStagingDetails AS TABLE
(
FirstName NVARCHAR(100),
LastName NVARCHAR(100)
);
Run Code Online (Sandbox Code Playgroud)
现在程序变成:
ALTER PROCEDURE dbo.[Insert_Header_Details_Tables]
@FileName [varchar](50) = null,
@VendorName [varchar](250) = null,
@Names dbo.TVPLabStagingDetails READONLY
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.LabStagingHeader([FileName], [VendorName])
SELECT @FileName, @VendorName;
DECLARE @ProcessID INT = SCOPE_IDENTITY();
INSERT dbo.LabStagingDetails([ProcessID], [LastName], [FirstName])
SELECT @ProcessID, LastName, FirstName FROM @Names;
END
Run Code Online (Sandbox Code Playgroud)
如果这种改变是不可能的,就推迟;如果这也失败了,那么您将需要使用一个输出参数,以便客户端可以传入@ProcessID
后续调用。但实际上,这是执行此操作的效率最低的方法。
其他一些花絮: