2009-03-21 13 views
0

私は単純なメッセージングプログラムを書いています。メッセージのテーブルがあります。メッセージのテーブルはユーザーが要求し、そのユーザーによって処理されます。どのユーザーが特定のメッセージを要求するかは予期されていないので、私が持っているすべての利用可能なメッセージのうち最初のものを選択し、そのメッセージを私が持っているテイクとしてマークするクエリが必要です。問題は、同時に2人のユーザーが同じメッセージを主張することを希望しないため、2つのステートメントを連続して実行し、プログラムに戻って次のプログラムを実行する必要はありません。文の間。セミコロンで区切って2つの連続した文を実行することができますが、最初の問合せで戻されたデータを2番目の問合せの一部として使用したいと考えています。変数は完璧ですが、私が知っている限り、SQLには存在しません。クエリ間に状態を保存する方法はありますか?状態が連続したSQLステートメント

答えて

0

取引がle dorfierが言うように、行くには良い方法ですが、alernativesがあります:あなたが最初の更新を行うことができ

、すなわち、ユーザIDまたは同様のメッセージにタグを付けるには。あなたは、SQL風味youreの使用に言及いけないが、MySQLで、私はそれがこのようなものに見えると思う:

WITH q AS (
    SELECT TOP 1 
    FROM message m 
    WHERE user_id = 0 
) 
UPDATE q 
SET user_id = 1 
:MS SQLで

UPDATE message 
SET user_id = ... 
WHERE user_id = 0 -- Ensures no two users gets the same message 
LIMIT 1 

を、それはの線に沿って何かだろう

/B

1

クエリ間に状態を保存する方法はありますか?

いいえ、SQLは手続き型言語ではありません。 2つのクエリを1つのクエリとして書き直すことができます(必ずしも可能ではなく、可能であっても価値がないことが多い)か、手続き型言語でそれらを結合します。多くのSQLサーバーは、このための組み込み言語(「ストアドプロシージャ」)を提供していますが、アプリケーションで実行することもできます。

問題は、私は、同じメッセージを請求すると同時に、

使用のロックを、それを使用して2人のユーザーを望んでいないこと、です。私はあなたが使用しているSQLサーバを知っていませんが、利用可能であれば、あなたが望むものであるようにSELECT ... FOR UPDATEという音を使用しています。

2

これはBEGIN TRANとCOMMIT TRANのためのものです。保護したいステートメントをトランザクション内に配置します。

0

おそらく一時テーブルを使用できます。

0

SQL自体には変数はありませんが、ほとんどすべてのRDBMS SQL拡張機能が(ほとんどの場合)?しかし、私はそれだけであなたの問題を解決する方法は本当によく分かりません。

前述のように、トランザクションは、関連のない2つのステートメントをまとめてトリック効果的にグループ化します。 の場合、デフォルトの取引レベルはとなります。 (Most?)RDBMSサーバーのデフォルト・トランザクション・レベルはREAD COMMITTEDです。これは、ユーザー2がユーザー1が読み取ったのと同じ行を読み取ることを妨げません。そのためには、REPEATABLE READまたはSERIALIZABLEを使用する必要があります。

これは古典的な並行性の問題です。一般に、それを処理する2つの方法は、悲観的なロックまたは楽観的なチェックです。REPEATABLE READトランザクションは悲観的なものになり(必要であるかどうかにかかわらずロッキング経費がかかる)、@@ ROWCOUNTをチェックすることは楽観的です(@@ ROWCOUNT = 0のときにはうまくいくと思います)。

通常、楽観的(ロックは高価です)を使用し、タイムスタンプまたはフィールドの組み合わせを使用して、私たちが思ったデータを変更していることを確認します。したがって、私の提案は、rowversionまたはtimestampフィールドをインクルードし、それをUPDATEステートメントに渡すことです。次に、@@ ROWCOUNTをチェックして、レコードを更新したかどうかを確認します。あなたがしなかったなら、戻って別のメッセージを選んでください。擬似コードで:

int messageId, byte[] rowVersion = DB.Select(
    "SELECT TOP 1 
     MessageId, RowVersion 
    FROM Messages 
    WHERE 
     User IS NULL"; 

int rowsAffected = DB.Update(
    "UPDATE Messages SET 
     User = @myUserId 
    WHERE 
     MessageId = @messageId 
     AND RowVersion = @rowVersion", 
    myUserId, messageId, rowVersion 
); 
if (rowsAffected = 0) 
    throw new ConcurrencyException("The message was taken by someone else"); 

あなたの特定のステートメントに応じて、あなただけのWHEREあなたのUPDATEステートメントで句「ユーザーIDがNULL IS」繰り返して逃げることができるかもしれません。これはBrimstedtのソリューションに似ていますが、でも、行が実際に更新されたかどうかを確認するには、@@ ROWCOUNTを確認する必要があります。

関連する問題