ストアドプロシージャを介してTSQLシーケンスをエミュレートする


17

TSQLシーケンスをエミュレートするストアドプロシージャを作成する必要があります。つまり、呼び出しごとに常に増加する個別の整数値を提供します。さらに、整数が渡された場合、結果がこれまでにないか、利用可能な次に高い整数がなかった場合、その値を返す必要があります。言うまでもなく、このSPを同時に呼び出す複数のクライアントが存在する可能性があります。

列MetaKey varchar(max)およびMeatValueLong bigIntを持つ表MetaInfoが与えられます。「Internal-ID-Last」のMetaKeyを持つ行には、割り当てられた最後の最高値が含まれることが予想されます。次のストアドプロシージャを作成しました。

CREATE PROCEDURE [dbo].[uspGetNextID]
(
  @inID bigInt 
)
AS
BEGIN
    SET NOCOUNT ON;

    BEGIN TRANSACTION

    UPDATE MetaInfo WITH (ROWLOCK) 
      SET MetaValueLong = CASE 
                            WHEN ISNULL(MetaValueLong,0) > @inID THEN MetaValueLong+1 
                            ELSE @inID+1
                          END 
    WHERE MetaKey = 'Internal-ID-Last'

    SELECT MetaValueLong 
    FROM MetaInfo
    WHERE MetaKey = 'Internal-ID-Last'

    COMMIT TRANSACTION 

END

私の質問は、このストアドプロシージャが期待どおりに機能するかどうかです(すべての呼び出し元に一意の結果が割り当てられます)。


@all:FYI、SOに関するこのQによって生成:stackoverflow.com/q/6342732/27535
gbn

回答:


8

私は見てきましたが、MS自身がロックのないソリューションを提供します

http://blogs.msdn.com/b/sqlcat/archive/2006/04/10/sql-server-sequence-number.aspx

これはロックヒントのない単純な更新ですが、ロック/デッドロックと言います。

これについても大したことはありません。

ROWLOCKにUPDLOCKを追加したいと思います(「キューとしてのテーブル」(SO)によるが、READPASTはなし)。これにより、2番目のプロセスが読み取りを開始した場合の分離が向上します。

ただし、すべてのプロセスが同じ行を読み書きすることを望むという事実から、私は自分自身を推測します。READPASTは安全な同時実行を許可しますが、この場合は役に立ちません。

注:2回目の選択の代わりにOUTPUT句を使用すると、トランザクションは不要になります。

HTH ...


1
あなたは私を打ち負かした。SQL Server 2011にはSEQUENCE機能が含まれているため、独自に発明する要件はすぐに(時間前ではなく)なくなるはずです。
nvogel

@dportas:確かに。また、より良い実行:dba.stackexchange.com/q/1635/630
gbn

@dportas-SEQUENCEは入力要件を許可できますか?この機能のクイックリードでは、その機能は見当たりませんでした。
ホーガン

1

次のものがありません

1. SET XACT_ABORT
2. Exception Handling (Try Catch)

はい、それはあなたの条件を満たしている必要があります。トランザクションでこのような状況が発生すると、複数のインスタンスが作成され、その後、すべての呼び出し元に一意の結果が割り当てられます


選択の前にコミットする可能性がない場合、別の呼び出しで保存された結果を選択しますか?
ホーガン

それについてはわかりません。しかし、あなたは正しいようです。確認する必要があります。ありがとう。BTW +1、良い質問...-

0

シリアル化を必要としない、よりスケーラブルなソリューションは次のとおりです。

CREATE PROCEDURE [dbo].[uspGetNextID]
(
  @inID BIGINT OUT
)
AS
      SET NOCOUNT ON
      SET IDENTITY_INSERT SequenceTable ON;
      INSERT INTO SequenceTable (id) VALUES (@inID);
      SET IDENTITY_INSERT SequenceTable OFF;
      INSERT INTO SequenceTable DEFAULT VALUES;
      DELETE FROM SequenceTable WITH (READPAST);
      SET @inID = SCOPE_IDENTITY();
RETURN;

OPには、複雑な値をユーザーが渡すことを許可する要件があります
...-gbn

@dportas-ありがとう。私はこのアプローチを知っていましたが、入力パラメーターのためにここでは機能しません。
ホーガン

@Hogan、入力パラメータを処理するように提案を修正しました。ただし、ほとんどテストされていません。
-nvogel

@dportas-500のinIDで呼び出されたA、50のinIDで呼び出されたB-アクション-Aは51で戻り、Bは52で戻ります。要件は失敗します。
ホーガン

面白い。異なる結果が得られます(2008r2)。DDLを使用して完全な再現を投稿し、ビルド/バージョンを記載してください。
-nvogel
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.