質問

私は、ユーザーが主張することができ、ものは、そのユーザーがそれらに行われてきたメッセージのテーブルがあり、単純なメッセージングプログラムを、書いています。与えられたメッセージを主張し、私はクエリは私も持っているすべての利用可能な私が持っているメッセージ、その後、1取るように、そのメッセージをマークするために、最初のを選択することになるでしょうどのユーザー運命づけされていません。問題は、私は、同じメッセージを請求すると同時に、それを使用して2人のユーザーを望んでいないということですので、私はで次に実行するかを調べるために、プログラムに戻って移動することなく、連続して二つの文を実行したいです文の間。私は、セミコロンで区切ることにより、2つの連続したステートメントを実行することができると信じて、私は第二の一環として、最初のクエリで返されたデータを使用します。変数は完璧になる、しかし限り、私は承知しているとして、彼らは、SQLには存在しません。私は、クエリの間で状態を保存することができます任意の方法はありますか?

役に立ちましたか?

解決

取引がle dorfierが言うように、行くには良い方法ですが、alernativesがあります:

あなたは、すなわち、ユーザIDまたは同様のメッセージをタグ付け、最初の更新を行うことができました。あなたが使用しているSQLの風味youreの言及いけないが、MySQLで、私はそれは次のようになりたいと思います。

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

MS SQLで、それはの線に沿って何かがあると思います:

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

/ B

他のヒント

このはTRANをBEGINとTRANをコミットしているものです。あなたがトランザクション内で保護したい文を置きます。

  

私は、クエリの間で状態を保存することができます方法はありますか?

はありません。 SQLは、手続き型言語ではありません。あなたは(それが可能であっても常に可能ではない、多くの場合ではないそれだけの価値)、単一のクエリとして、あなたの2つのクエリを書き換え、または手続き型言語でそれらを一緒に接着することができます。多くのSQLサーバは、この(「ストアドプロシージャ」)のための組み込みの言語を提供し、またはあなたのアプリケーションでそれを行うことができます。

  

問題は、私は同時に、それを使用して2人のユーザーが同じメッセージを主張したくないということです。

ロックを使用してください。それが利用可能である場合、私は、あなたが使用しているSQLサーバーを知っているが、それはあなたが望むものだけになりますようにSELECT ... FOR UPDATEを使用すると鳴りません。

あなたはおそらく一時テーブルを使用することができます。

SQL自体は、変数を持っていませんが、(ほとんど?)全てのRDBMSのSQL拡張機能を行います。しかし、私はそれだけでは、あなたの問題を解決するだろうか本当にわからない。

で述べたように、トランザクションは、トリックを行います - 効果的に関連のない2つの文を一緒にグループ化します。 はしかしの、デフォルトのトランザクションレベルは、の動作しません。 (ほとんど?)RDBMSサーバーのデフォルトのトランザクションレベルはREAD COMMITTEDです。すなわち、ユーザ1リードと同じ行を読んでから、ユーザ2を妨げません。そのためには、REPEATABLE READまたはSERIALIZABLEを使用する必要があると思います。

これは古典的な同時実行の問題です。一般的に、それを処理する2つの方法は、悲観的ロックまたは楽観チェックされています。 REPEATABLE READトランザクションは、(それが必要だったかどうか、ロックの費用を負担する)、および@@ ROWCOUNTをチェック楽観されて悲観的になります(それがうまくいくと仮定しますが、賢明な何かをやったときに@@ ROWCOUNT = 0)。

は通常、我々は(ロックが高価である)楽観を使用し、我々は我々が思っデータを変更していることを保証するために読んでフィールドのタイムスタンプまたは組み合わせを使用しますか。だから、私の提案は、rowversionまたはタイムスタンプフィールドを含めると、あなたの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 の行が実際に更新されたかどうかを確認します。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top