1

登録ページを作っています。2 人のユーザーが同時に同じユーザー名で登録する同時実行の問題について心配しています (これは非常にまれなことですが、コードに小さな欠陥があるのは嫌いです)。

したがって、私のアプローチは、ユーザー名が存在するかどうかを確認し、存在しない場合は新しい行を挿入することです。私はPDOを使用しています

私が試したこと

私はトランザクションを使用していますが、ここで私の質問からPHP PDO を使用したトランザクションは同時実行でどのように機能しますか? 2 つのトランザクションが同時に読み取ることができるようです

  • select...for update更新していないので使えないと思います。実際、まだ存在しない場合は、新しいエントリが追加される「架空の」行をロックする必要があります
  • いくつかの例をグーグルで検索してみましたが、上記の同時実行の問題を処理していないようですhttp://php.about.com/od/finishedphp1/ss/php_login_code_2.htm

解決?

私はググってUNIQUEユーザー名フィールドに制約を追加しましたが、MySQL エラーに依存してトランザクションをロールバックしたくありません。私は専門家ではありませんが、エレガントに感じません。では、純粋な MySQLを使用する方法はありますか?insert if not exists

さて、これが本当に進むべき道なら、いくつか質問があります。警告がスローされた場合、クエリは停止しますか? 同様に、PHP + MySQL トランザクションの例の例に示されている例外をスローしますか? それとも、完全なエラーのみが例外をスローしますか?

また、ここでは PHP で MySQL の警告を検出して処理できますか? その警告はPHP出力に何らかの形で表示されますが、「目に見えるMySQLエラー」が発生したことはありません. 質問は、私のコードのように PDO を使用していませんでしたが、ただ疑問に思っていました。MySQL のエラーと警告は html 出力になりますか? それとも、私が電話した場合にのみ何かを言いますerrorInfo()か?

ありがとう

4

4 に答える 4

2

純粋なMySQLに存在しない場合に挿入する方法はありますか?

最善の方法は、一般に UNIQUE 制約に依存することと考えられています。ただし、他の選択肢があります。

警告がスローされた場合、クエリは停止しますか?

必ずしもそうではありませんが、特定のケースでは、 UNIQUE 制約を持つ複製を挿入しようとしても、何があってもうまくいきません。

MySQL のエラーと警告は html 出力になりますか?

いいえ、自動的ではありません。自分で処理する必要があります。操作が例外をスローし、例外ハンドラーが設定されている場合、それは自然に発生しますが、処理することもできます。

とにかく、厳密に言えば、フィールドの 1 つが重複しており、UNIQUE 制約があるテーブルにエントリを挿入しようとしても、エラーに関係なく成功しません。エラーを処理する方法はあなた次第ですが、PDO はこれらのケースで具体的に例外をスローするため、次のように簡単にすることができます。

try {
    //Insert user here

} catch(Exception $e) {
    if(($PDO->errorCode() == 23000) || ($PDOStatement->errorCode() == 23000)) {
        //Duplicate, show friendly error to the user and whatnot
    }
} 

ここでは重複の最も一般的な SQLSTATE エラー コード 23000 を使用しましたが、ほとんどすべてを確認できます。mysql のサーバー エラー コードのリストを次に示します。

于 2012-06-23T18:54:16.233 に答える
2

アップデート

2 つの一意のフィールドがあり、どちらも null にすることを許可したくない場合 (1 つのテーブルを "null 可能" に設定し、2 つのクエリを実行できることがわかっているため)、どちらが台無しになっているかを確認する方法はありますか? ユーザー名または電子メールが取得されたかどうかをユーザーに知らせたいのと同じように。単一の挿入を行うと、どれが失敗したかわかりません

さらに、ステートメントが 2 つある場合 (最初にユーザー名を挿入し、失敗したかどうかを確認し、次に電子メールで同じことを試す)、一度に 1 つのエラーしか検出できません。ユーザー名が失敗してロールバックした場合、メールに対して同じことを試みることはできません(メールが存在する場合でも、最初のクエリが失敗したかどうかを確認する必要があるため、冗長になります)編集:できると思いますネストされた try...catch ステートメントを使用すると機能します

私はあなたがこれを考えすぎていると思います。ユーザーごとに異なるエラーを表示できますが、実際には一度に 1 つしか検出できません。前のクエリを仮定すると (すべての値を一度に挿入するため):

try {
  $stmt = $db->prepare($sql);
  $stmt->execute(array(
    ':username' => $username,
    ':email' => $email,
    ':password' => $password
  ));

  $db->commit();
} catch (PDOException $e) {
  $db->rollBack();
  if($e->getCode() == 23000) {

    // lets parse the message

   if(false !== strpos('email', $e->getMessage())) {
       echo 'Email "'.  $email . '" already exists';
    } else if(false !== strpos('username', $e->getMessage()) {
       echo 'Username "'. $username .'" taken';
    }

  } else {
     // not a dupe key rethrow error
     throw $e;
  }
}

エラーメッセージは次のようになります。

Duplicate entry 'THE_VALUE_YOU_TRIED_TO_INSERT' for key THE_KEY_NAME

どの列であったかについてより意味のあるレポートを取得する方法は、テーブルを作成するときに意味のある名前を付けてインデックスに名前を付けることです。たとえば、次のようにします。

CREATE TABLE the_table (
   `id` integer UNSIGNED NOT NULL AUTO_INCREMENT
   `username` varchar(30),
   `email` varchar(100),
   `password` varchar(32),
   PRIMARY KEY (`id`),
   UNIQUE KEY `uk_email` (`email`),
   UNIQUE KEY `uk_username` (`username`)
); 

エラーメッセージは次のようになります。

Duplicate entry 'THE_VALUE_YOU_TRIED_TO_INSERT' for key uk_username

また

Duplicate entry 'THE_VALUE_YOU_TRIED_TO_INSERT' for key uk_email

これは簡単に解析できます。

この方法に代わる唯一の実際の方法は、挿入する前にテーブルで選択を実行して、値がまだ存在しないことを確認することです。


PDO を使用している場合、PHP の警告は表示されません... 例外のみで、キャッチされない場合は標準の php エラーが生成されます。display_errorsオフにした場合、画面には出力されず、エラーログにのみ出力されます。

insert if not exists一意のキーを使用してから例外をキャッチする方法があるとは思わない:

$db = new PDO($dsn, $user, $pass);
$sql = "INSERT INTO the_table (username, email, etc) VALUES (:username,:email,:password)";

$db->beginTransaction();
try {
  $stmt = $db->prepare($sql);
  $stmt->execute(array(
    ':username' => $username,
    ':email' => $email,
    ':password' => $password
  ));

  $db->commit();
} catch (PDOException $e) {
  $db->rollBack();
  if($e->getCode() == 23000) {
    // do something to notify username is taken
  } else {
     // not a dupe key rethrow error
     throw $e;
  }
}
于 2012-06-23T18:55:31.857 に答える
1

重複がないことを確認する最善の方法はUNIQUE、ユーザー名フィールドで制約を使用することです。MySQL は間違いなく同じ値を持つ 2 番目の行を挿入しないため、そのためのトランザクションは不要になります。

このソリューションがエレガントに見えない理由はわかりませんが、私の意見では、作業を大幅に簡素化するための非常に良い方法です。これがデータの制約である場合は、データベース サーバーにその問題を処理させるのが理にかなっています。

トランザクションで挿入クエリを実行すると、エラーが発生した場合、実行が停止され、サーバーがロールバックを実行します。処理が必要な PHP のエラーも表示されます。

于 2012-06-23T18:55:57.960 に答える
1

このタスクを実行するのに最適な位置にあるため、データベースにこれを処理させます。

一意の制約を使用しますが、

insert ignore into users...

http://dev.mysql.com/doc/refman/5.5/en/insert.html

エラーは発生しません。影響を受けた行をチェックして、行が挿入されたかどうかを確認できます。

于 2012-06-23T19:24:42.870 に答える