2012-04-22 6 views
1

トランザクションのヘッダー情報を含むテーブルがあります。トランザクションは異なるプロジェクトに属します。 I列を有するヘッダーでテーブル内の行を直列化する

rhguid - uniqueidentifier 
rhserial - int 
rh_projectID - int 

まず、私は行を挿入(複数の列があります)

そして私はそのプロジェクトのシリアル番号計算:

update responseheader 
set rhSerial = 1 + (select isnull(max(rhSerial), 0) 
        from responseheader 
        where (rhstatus = 0) AND (rh_projectID = 1234)) 
where 
    (rhGUID = <preassignedGUID>); 

を但しプロジェクトのために同時に多くのトランザクションが発生した場合、重複するrhserialの値が見つかりました。

私はSQL Server 2008の

と従来のASPでこれをやっているが、より良い方法はありますか?

+0

'rhserial'カラムを' INT IDENTITY'カラムにして、連続番号を増やしてSQL Serverに残すことはできませんか?これは本当に最善の方法です。面倒な手間や煩わしさはありません。SQL Serverに作業をさせてください。 –

+0

いいえ。各プロジェクトにはシリアル番号が必要です。したがって、プロジェクト1234は1から上に向かってrhserialを持ちます。また、プロジェクト1235には、1つ上から上に向かってrhserialがあります。 rhserialはテーブル全体で一意ではありません。 – derekcohen

+1

この場合、(1)各プロジェクトの現在のシリアル番号を持つテーブルと、(2)各プロジェクトの次の有効な番号を取得するためのスレッドと並行処理に安全なメソッド(ストアドプロシージャなど)が必要です。その "シリアルテーブルなし"。 'SELECT MAX()+ 1'を使うだけで、**負荷のかからない**安全です。 [この他のSOの質問とこれを行う方法を示すRemus Rusanuの答えを参照してください](http://stackoverflow.com/questions/5083846/sql-server-2005-using-generated-sequences-instead-of-identity-columns ) –

答えて

1

例では、トランザクションを使用しているように見えません。私の推測では、ステートメントのSELECT部分​​はとして表示されていますが、これはREADCOMMODTEDです。そうでなければ重複は見られません。 ADOとトランザクションを開始する方法はありますが、代わりにストアドプロシージャを使用することをお勧めします。

はこのようなものを実装してみてください。

CREATE PROC dbo.ResponseHeader_Insert 
    <more data to insert>, 
    @ProjectID INT, 
    @Status SMALLINT 
as 

insert responseheader (column names here) 
select <param values here>, isnull(max(rhSerial), 0) + 1 
from responseheader 
where (rhstatus = @Status) AND (rh_projectID = @ProjectID)) 

これはyaのために動作しない場合は、シーケンステーブル(各シーケンスに1つ)を作成してみてください。

create table <tablename> (
     SeqID int identity(1,1) primary key, 
     SeqVal varchar(1) 
) 

次のIDを取得するために、プロシージャを作成します。

create procedure GetNewSeqVal_<tablename> 
as 
begin 
     declare @NewSeqValue int 

     set NOCOUNT ON 

     insert into <tablename> (SeqVal) values ('a') 
     set @NewSeqValue = scope_identity() 
     delete from <tablename> WITH (READPAST) 
return @NewSeqValue 
end 

作成する必要があるか、その場でシーケンスを作成したい、あまりにも多くのシーケンステーブルがある場合は、この方法を試してみてください。

Create table AllSequences (
     SeqName nvarchar(255) primary key, -- name of the sequence 
     Seed int not null default(1), -- seed value 
     Incr int not null default(1), -- incremental 
     Currval int 
) 
Go 

create procedure usp_CreateNewSeq 
     @SeqName nvarchar(255), 
     @seed int = 0, 
     @incr int = 1 
as 

begin 

     declare @currval int 
     if exists (
      select 1 from AllSequences 
      where SeqName = @SeqName) 

     begin 
      print 'Sequence already exists.' 
      return 1  
     end 

     if @seed is null set @seed = 1 
     if @incr is null set @incr = 1 
     set @currval = @seed 

     insert into AllSequences (SeqName, Seed, Incr, CurrVal) 
     values (@SeqName, @Seed, @Incr, @CurrVal) 
end 
go 


create procedure usp_GetNewSeqVal 

     @SeqName nvarchar(255) 
as 

begin 
     declare @NewSeqVal int 
     set NOCOUNT ON 

     update AllSequences 
     set @NewSeqVal = CurrVal = CurrVal+Incr 
     where SeqName = @SeqName 

     if @@rowcount = 0 begin 
      print 'Sequence does not exist' 
      return 
     end 
     return @NewSeqVal 
end 
go 
+0

最初のストアドプロシージャメソッドは、未知の長さの独立したシーケンスが何千もあるため、最適に動作します。あなたのSPは、挿入物ではなくむしろ行全体のものであると思われます。 SPにいることは取引の問題を克服していますか?ありがとう – derekcohen

+0

@derekcohen、それだけではなく、それは簡単です。 Begin Tran/Commit Tranを見てください。最初のストアドプロシージャにbegin/commit tranを追加しなかったのは、insert文が1つしかないからです。 ADOで同じことをすることができ、トランザクションの作成について心配する必要はありません。元のクエリは挿入/更新(2つのステートメント)でした。これらを単一のトランザクションで実行するには、begin/commit tranを使用するか、ADOを使用してトランザクションを開始する必要があります。 –

+0

ありがとうございました。 – derekcohen

関連する問題