2011-01-21 7 views
2

私は自宅に持っているアプリケーションに関するSQL Serverのロックに関する質問があります。アプリケーションはデータの送信を受け取り、それらをSQL Serverテーブルに保持します。各サブミッションには、特殊なカタログ番号(表のIDフィールドとは無関係)が割り当てられています。これは、連続したアルファベットの数字です。これらの数値は別のテーブルから取得され、実行時には生成されません。だから、手順は、カタログ表から提出表にSQLテーブルロック

  1. データの挿入
  2. グラブ次の未使用のカタログ 数

提出テーブルに 提出にすべてのこれらのカタログ番号を割り当てています手順は同じストアドプロシージャで順番に行われます。

それは、しかし時には同じ秒で2回の提出を得ることができ、両方が同じカタログ番号を割り当てられ、黙示録のローカライズ版が少しずつ私たちの会社で発生します。

カタログ番号のオーバー割り当てを制限するにはどうすればよいですか?

+1

ストアドプロシージャの3つのステップはすべて、トランザクション内にカプセル化されていますか?トランザクションに設定されている独立性レベルは何ですか? – Thomas

+0

割り当てられたカタログ番号に隙間がある場合は、黙示録ですか?つまり、T1にカタログ番号が割り当てられてからエラーが発生した場合、そのカタログ番号がスキップされた場合にはロールバックされますか? –

+0

申し訳ありませんが離れて私の自身の質問を監視できませんでした。だから、1)いいえ、彼らは現在、トランザクションで、ちょうど同じsprocの中で次々に実行されていません。 2)隙間は理想的ではないが、黙示録の –

答えて

4

あなたの次のカタログ番号を取得する場合、例えば、それを見つけ、使用中としてそれをマークするあなたの時間を保護するためにロックを使用行:

set transaction isolation level REPEATABLE READ 
begin transaction 
select top 1 @catalog_number = catalog_number 
    from catalog_numbers with (updlock,rowlock) 
    where assigned = 0 
update catalog_numbers set assigned = 1 where catalog_number = :catalog_number 
commit transaction 
+0

-1から遠い。行ロックはトランザクションとは無関係であり、カタログ番号を一切保護しません。そのうちの1人がそれを使用する前に、2つのセッションが同じカタログ番号を選択しないようにするものはありません。 – Guffa

+0

@Guffa 'updlock'はこれを防ぎます。 'UPDATE .. TOPとOUTPUT'はselectを必要とせずにこれを行うことができます。 –

+0

@Martin:updlockは、トランザクションが終了するまでテーブルをロックします。両方のクエリにまたがるトランザクションがないので、トランザクションには独自のトランザクションがあるため、トランザクションとロックは2番目のクエリの開始前に終了します。 – Guffa

1

あなたはカタログ番号を生成するためのIDフィールドを使用することができ、

insert into Catalog() values() 
set @CatalogNumber = scope_identity() 

scope_identity機能は同じセッションで作成された最後のレコードのIDを返しますので、別々のセッションが同時にレコードを作成し、まだで終わることができます:あなたが安全に番号を作成し、取得することができ、そのよう正しいID。

ID番号フィールドを使用してカタログ番号を作成できない場合は、トランザクションを使用して、次の番号を判別して、テーブルにアクセスする別のセッションなしで作成する必要があります。

+0

+1これは、あなたがそれを書き直すの贅沢を持っている場合、明白な平凡な方法です – Andomar

+0

セッションは新しい行の作成を可能にするだけ現在の行をロックしないでしょうか? – ispiro

+0

@ispiro:IDフィールドを使用する場合、セッションで作成されたIDを取得できるので、別のセッションで同時にレコードを作成しても問題ありません。最初にIDを特定してから挿入する必要がある場合は、両方の手順を実行するまで他のセッションを待つことができるようにトランザクションが必要です。 – Guffa

0

私はアラクニッドの反応が好きです。また、これを達成するためにサブミット・テーブルに挿入トリガーを使用することもできます。トリガーは挿入のスコープ内にあり、トリガーにcatalog_numberを割り当てるロジックを効果的に組み込むことになります。あなたのオプションをここに入れたいだけでした。

0

ここに簡単な解決策があります。競合状態はありません。制限付きトランザクション分離レベルからのブロックはありません。しかし、おそらくT-SQL以外のSQLの方言では動作しません。

カタログ番号テーブルに割り当てられていないカタログ番号が入力されているようにするには、仕事で外的な力があると思います。

この技術はあなたのために働く必要があります:値を取得する「インターロック更新」と同じ種類の操作を行うだけで、のようなもの:

update top 1 CatalogNumber 
set in_use   = 1 , 
    @newCatalogNumber = catalog_number 
from CatalogNumber 
where in_use = 0 

はとにかく、次のストアドプロシージャは、ただ単に、それぞれの数をアップティック前のものを手渡す。より魅力的な値が必要な場合は、希望する値を得るために、選択した変換をインクリメント値に適用する計算列を追加します。

drop table dbo.PrimaryKeyGenerator 
go 
create table dbo.PrimaryKeyGenerator 
(
    id   varchar(100) not null , 
    current_value int   not null default(1) , 

    constraint PrimaryKeyGenerator_PK primary key clustered (id) , 

) 
go 
drop procedure dbo.GetNewPrimaryKey 
go 
create procedure dbo.GetNewPrimaryKey 

    @name varchar(100) 

as 

    set nocount     on 
    set ansi_nulls    on 
    set concat_null_yields_null on 
    set xact_abort    on 

    declare 
    @uniqueValue int 

    -- 
    -- put the supplied key in canonical form 
    -- 
    set @name = ltrim(rtrim(lower(@name))) 

    -- 
    -- if the name isn't already defined in the table, define it. 
    -- 
    insert dbo.PrimaryKeyGenerator (id) 
    select id = @name 
    where not exists (select * 
        from dbo.PrimaryKeyGenerator pkg 
        where pkg.id = @name 
        ) 

    -- 
    -- now, an interlocked update to get the current value and increment the table 
    -- 
    update PrimaryKeyGenerator 
    set @uniqueValue = current_value , 
     current_value = current_value + 1 
    where id = @name 

    -- 
    -- return the new unique value to the caller 
    -- 
    return @uniqueValue 
go 

それを使用する:@pkは EXEC @pk = dbo.GetNewPrimaryKey 'foobarに' のint型

宣言は、結果セットを返すか、返すために、それを国防省する

トリビアルを@pk選択 OUTPUTパラメータを使用して値を設定します。

関連する問題