9

レガシー データベースのテーブルにいくつかのレコードを挿入する必要がありますが、他の古いシステムで使用されているため、テーブルを変更することは解決策ではありません。

問題は、ターゲット テーブルに int 主キーがあるが ID 仕様がないことです。そのため、次に利用可能な ID を見つけて使用する必要があります。

select @id=ISNULL(max(recid)+1,1) from subscriber

ただし、問題が発生しないように、これを行っているときに他のアプリケーションがテーブルに挿入されないようにしたいと考えています。私はこれを試しました:

begin transaction
    declare @id as int
    select @id=ISNULL(max(recid)+1,1) from subscriber WITH (HOLDLOCK, TABLOCK)
    select @id
    WAITFOR DELAY '00:00:01'
    insert into subscriber (recid) values (@id)
commit transaction
select * from subscriber

SQL Management Studio の 2 つの異なるウィンドウで、1 つのトランザクションが常にデッドロックの犠牲者として強制終了されます。

私もSET TRANSACTION ISOLATION LEVEL SERIALIZABLE最初に試してみましたが、同じ結果でした...

次のIDを確実に取得して、他の誰か(または私!)が夢中になる危険を冒さずにそれを使用する方法についての良い提案はありますか?

これについて先に言及せずに申し訳ありませんが、これは SQL 2000 サーバーであるため、FOR UPDATE や OUTPUT などは使用できません。

更新:これは私のために働いた解決策です:

BEGIN TRANSACTION
    DECLARE @id int

    SELECT  @id=recid
    FROM    identities WITH (UPDLOCK, ROWLOCK)
    WHERE table_name = 'subscriber'

    waitfor delay '00:00:06'

    INSERT INTO subscriber (recid) values (@id)

    UPDATE identities SET recid=recid+1 
    WHERE table_name = 'subscriber'

COMMIT transaction

select * from subscriber

WAITFOR は、複数の接続を持ち、クエリを数回開始して同時実行を引き起こすためのものです。

回答してくれた Quassnoi と、貢献してくれた他のすべての人に感謝します! 素晴らしい!

4

4 に答える 4

10

別のテーブルを作成します。

t_identity (id INT NOT NULL PRIMARY KEY CHECK (id = 1), value INT NOT NULL)

単一の行で、この行をロックし、value必要になるたびに 1 ずつ増やしますIDENTITY

1 つのステートメントで新しい値をロック、インクリメント、および返すには、次を使用します。

UPDATE  t_identity
SET     value = value + 1
OUTPUT  INSERTED.value

更新したくない場合は、ロックしてから次を発行します。

SELECT  value
FROM    t_identity WITH (UPDLOCK, ROWLOCK)

これにより、トランザクションが終了するまでテーブルがロックされます。

t_identityをいじる前に常に最初にロックするとancient_table、デッドロックが発生することはありません。

于 2009-04-15T11:38:24.403 に答える
4

ID 列を持つ別のテーブルを追加し、この新しいテーブルと列を使用して、古いテーブルの ID 値を選択/生成します。

更新: INSERTS の頻度 (および既存の行数e ) に応じて、新しい IDENTITY 値をe+xにシードできます。ここでxは十分に大きくなります。これにより、従来の挿入との競合が回避されます。悲しい解決策、確かに不完全な解決策ですが、何か考えるべきことはありますか?

于 2009-04-15T11:34:31.123 に答える
0

2 番目のプロセスは最初のプロセスが完了するまで待機する必要があるため、ここでデッドロックが発生することはありません。あなたの問題は、トランザクションを作成し、そのトランザクション中に別のロックを追加していることです。

また、ID を取得してから 2 つの別々のステートメントで使用していますが、1 つのソリューションですべてを実行できます。

set transaction isolation level serializable
begin transaction
    insert into subscriber (recid) 
       SELECT (select ISNULL(max(recid)+1,1) from subscriber)
commit transaction
select * from subscriber

これにより、挿入の一貫性のみが保証されます。ただし、レガシー アプリケーションもこのテーブルを使用していると指定した場合、新しいレコードを挿入するときにこれと競合しないと確信できますか?

于 2009-04-17T08:47:25.450 に答える