使用复合主键选择刚刚插入的记录

时间:2011-04-28 21:47:43

标签: sql sql-server sql-server-2008 composite-primary-key

我有一个带有复合主键的表,排列如下:

CREATE TABLE [dbo].[mytable]
(
    [some_id] [smallint] NOT NULL,
    [order_seq] [smallint] NOT NULL,
    -- etc...
)

这两列都是主键的一部分(它实际上是真实表上的4部分PK,但我为了示例而简化了它)。没有列是身份。我正在编写一个存储过程,在给定order_seq的下一个some_id处插入新记录:

CREATE PROCEDURE some_proc
(
    @some_id smallint,
    @newSeq smallint OUTPUT
)
AS
BEGIN
    insert into mytable (some_id, order_seq)
    values 
    (
         @some_id, 
         (select max(order_seq) + 1 from mytable where some_id = @some_id)
    )

    set @newSeq = /* order_seq of the newly-inserted row */
END

我需要知道如何设置@newSeq。我想避免在插入后运行select查询,因为我不想遇到并发问题 - 我被禁止锁定表或使用事务(不要问)。

据我所知,我不能使用SCOPE_IDENTITY(),因为没有列是标识。如何正确设置newSeq

4 个答案:

答案 0 :(得分:5)

首先,如果PK包含四列,则每个插入必须包含所有四列。其次,如果您使用的是SQL Server 2005 +

,则可以查看Output子句
Declare @NewSeqTable Table( Order_Seq int not null )

Insert MyTable( some_id, order_seq, otherPkCol, otherPkCol2 )
Output inserted.order_seq Into @NewSeqTable
Select @some_id, Max( order_seq ) + 1, otherPkCol, otherPkCol2
From MyTable
Where some_id = @some_id

Select Order_Seq
From @NewSeqTable

OUTPUT Clause (Transact-SQL)

答案 1 :(得分:1)

除非我弄错了,否则你已经有了并发问题,因为“mytable”中的“select max(order_seq)+ 1”。我说你提出的问题(无法锁定或做交易)是不可能的。

如果order_seq不是smallint,我会说生成一个非常大的随机数作为你的order_seq,并重新生成(可能是罕见的)插入异常。但这对于基本上不可行的情况来说是极端的解决方法。

唯一的另一种选择(我警告你,这很奇怪)是制作一个具有标识列的小型虚拟表,然后基本上将生成的id复制到newSeq中。

答案 2 :(得分:1)

这里的答案取决于系统中的大小/并发问题。如果您对并发性问题不确定,则假设访问是多线程的。

单线程

如果你的系统很小或者你可以确定只有一个线程会一次触摸这个功能,那么下面的内容就可以了:

CREATE PROCEDURE some_proc ( @KeyPart1 smallint, @newSeq smallint OUTPUT ) 
AS

DECLARE @KeyPart1 int
DECLARE @KeyPart2 int


SET @KeyPart1 = (SELECT <whatever your logic is here>)
SET @KeyPart2 =  select max(order_seq) + 1 from mytable where some_id = @KeyPart1

insert into mytable (some_id, order_seq)
values  ( @KeyPart1, @KeyPart2 )

set @newSeq = @KeyPart2

多线程访问

如果无法保证只有一个线程会访问proc,那么您的代码中需要一个事务。根据您分享的内容,您似乎需要SERIALIZABLE次交易。 SERIALIZABLE是SQL Server中可用的最少并发(且最具保护性)的事务。由于您执行标识max的读取,因此需要序列化以防止可能会改变结果的幻像插入。

虽然你可能想要错误处理,但是像下面这样的程序应该有效......

CREATE PROCEDURE some_proc ( @KeyPart1 smallint, @newSeq smallint OUTPUT ) 
AS

DECLARE @KeyPart1 int
DECLARE @KeyPart2 int

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN

SET @KeyPart1 = (SELECT <whatever your logic is here>)
SET @KeyPart2 =  select max(order_seq) + 1 from mytable where some_id = @KeyPart1

insert into mytable (some_id, order_seq)
values  ( @KeyPart1, @KeyPart2 )

set @newSeq = @KeyPart2

COMMIT TRAN

答案 3 :(得分:0)

为什么不首先分配给@newSeq,然后在插入中使用@newSeq变量?