Выберите только что вставленную запись с помощью составного первичного ключа - PullRequest
5 голосов
/ 29 апреля 2011

У меня есть таблица с составным первичным ключом, расположенная примерно так:

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

Оба эти столбца являются частью первичного ключа (на самом деле это четырехкомпонентный ПК в реальной таблице, но я упростил его для примера). Ни один из столбцов не является личностью. Я пишу сохраненный процесс, который вставляет новую запись в следующий 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. Я хотел бы избежать запуска запроса выбора после вставки, потому что я не хочу сталкиваться с проблемами параллелизма - мне запрещено блокировать таблицу или использовать транзакцию (не спрашивайте).

Насколько я знаю, я не могу использовать SCOPE_IDENTITY(), потому что ни один из столбцов не является тождественным. Как правильно установить newSeq?

Ответы [ 4 ]

5 голосов
/ 29 апреля 2011

Во-первых, если PK содержит четыре столбца, то каждая вставка должна включать все четыре столбца.Во-вторых, вы можете изучить предложение Output, если вы используете SQL Server 2005 +

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 (Transact-SQL)

1 голос
/ 29 апреля 2011

Ответ здесь зависит от размера / проблем параллелизма в вашей системе.Если вы НЕ УВЕРЕНЫ в отношении проблем параллелизма, предположите, что доступ является многопоточным.

Однопоточный

Если у вас небольшая система или вы можете быть уверены, что только один поток будет касаться этой функции одновременно, тогда будет работать что-то вроде следующего:

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

Многопоточный доступ

Если вы не можете быть уверены , что толькоодин поток будет обращаться к процессу, затем вам понадобится транзакция в вашем коде.Из того, что вы поделились, кажется, что вам понадобится транзакция 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
1 голос
/ 29 апреля 2011

Если я не ошибаюсь, у вас уже есть проблемы с параллелизмом из-за оператора "select max (order_seq) + 1 from mytable". Я бы сказал, что проблема в том виде, в каком вы ее поставили (невозможность заблокировать или выполнить транзакции) невозможна.

Если бы order_seq не был smallint, я бы сказал, сгенерировать очень большое случайное число как ваш order_seq и сгенерировать на (предположительно редких) исключениях вставки. Но это крайнее решение для практически неработоспособной ситуации.

Единственная другая альтернатива (и я предупреждаю вас, что это странно) - создать небольшую фиктивную таблицу, в которой есть столбец идентификаторов, а затем скопировать сгенерированный идентификатор в newSeq.

0 голосов
/ 29 апреля 2011

Почему бы вам сначала не присвоить @newSeq, а затем использовать переменную @newSeq во вставке?

...