質問: $dbh->prepare('INSERT INTO users(email,pass,login_time) VALUES (:email, :pass, :login_time)'); $stmt->execute(array(':email' => $email,':pass' => $pass, ':login_time' => date('Y-m-d H:i:s'))); header("Location:mypage.php"); } ``` ### 考え このコードはバリデーションチェックをクリアし$err_msgに何も代入されていない状態です。 考えはこうです。 新規登録時にDB接続した際にSELECTでemailを検索する。 if文を使用し1件でも検索結果が出れば、MSG06をhtml内で表示する。 0件ならINSERTし新規登録を行いmypage.phpへアクセスする。 このような考えです。 ですが、重複チェックができず同じアドレスでも新規登録されてしまいます。 このような場合どのような理解でコードを書けば良いでしょうか?
回答: te2jiさんの回答は、おそらく敢えて初歩的なところに焦点を当てた回答かと思いますが、実際はこれでは不十分です。 SELECTによるチェックとその後のINSERTには時間差があるので、同時に同じメールアドレスを挿入しようとすると、重複が生じる場合があります。この種の問題は、TOCTOU(Time of check - time of use)競合と呼ばれます。 ではどうするのが定石かというと、SQLデータベースにはユニーク制約というものをつけられるので、それを活用することが一般的です。そうすると、同じメールアドレスでINSERTしようとすると、二番目以降はエラーとしてINSERTできなくなります。 現実には、ユニーク制約がついていないケースは著名ソフトウェアにも多いので、以下のような質問でその場合の「定石」を質問したことがあります。 一意制約のない列の一意性を保証してINSERTする方法 回答は非常に興味深いものばかりですが、やや理解が難しいと思います。なので、まずはユニーク制約をつけることを考えた方がよろしいかと思います。 ユニーク制約をつけるには、以下のようなSQL文で可能です。VARCHARの文字数は適宜変更してください。 ``` ALTER TABLE users MODIFY COLUMN email VARCHAR(150) UNIQUE KEY; ```