2017-02-01 5 views
0

同時クエリが混乱するのを防ぐためにクエリを作成しています。 私はかなり良い方法があると確信しています。それが私がここにいる理由です。INSERTステートメントで複数のSELECTを使用して競合状態を防止する

基本的には、テーブルの列は次のようになります。最後の場合

  • :私はここに、新しい行を挿入すると

    id  : INT(10) unsigned auto_incremented 
    number : INT(6) unsigned zerofill 
    date : DATETIME 
    

    私はその値を設定する必要があるかであります挿入された行のの日付は今日、の番号 =最後に挿入された行の番号+1

  • E最後に挿入された行の日付は昨日であるかの前に、私が最初に考えた後、 = 1

最後の行の日付と番号を選択し、私の挿入を行い、その後、PHPを使用して、新しい番号を設定します。

$pdo->beginTransaction(); 

$stmt = $pdo->prepare("SELECT `number`, `date` FROM `table` ORDER BY `id` DESC LIMIT 1"); 
$stmt->execute(); 
$infos = $stmt->fetch(PDO::FETCH_ASSOC); 

$date = date("Y-m-d H:i:s"); 
if (date("Ymd", strtotime($infos['date'])) < date("Ymd", strtotime($date))) { 
    $number = 1; 
} else { 
    $number = $infos['number'] + 1; 
} 

$stmt2 = $pdo->prepare("INSERT INTO `table` (`number`, `date`) VALUES (:number, :date)"); 
$stmt2->execute(array(":number" => $number, ":date" => $date)); 

$pdo->commit(); 

別のスレッドが同じ番号を選択して、それと同じ番号を挿入しようとするのを防ぐのに十分なトランザクションはありますか?

親愛なる友人のGoogleとSOを使っても、私は自分自身でその質問に答えることができなかったので、「私はMySQLですべての作業を行うことはできないのですか?次のクエリを出しました:

INSERT INTO `table`(`number`, `date`) 
SELECT IF(
    DATE((SELECT `date` FROM `table` ORDER BY `id` DESC LIMIT 1)) < CURDATE(), 
    '1', 
    (SELECT `number` + 1 FROM `table` ORDER BY `id` DESC LIMIT 1) 
), NOW() 

これは良い方法ですか?

何かをする前にテーブルをロックするだけでいいですか?

私はこれがどのように行われるべきか見るにはあまりにも愚かですか?

+0

日付をインクリメントする順番で番号を挿入しようとしていると判断した場合、日付と番号の列に複合一意索引を作成し、2つの並行クエリが実行されている同じ日に同じ番号を入力してください。 –

+0

さて、それは単なるメモでした。私はトランザクションが他のトランザクション状態に依存しているとは思わない。一度に30人のユーザーが読み込まれた場合は、30人のユーザーが最初に終了する29個のトランザクションすべてを必要とします。私はあなたが時間を通過することを許可している 'update'を'選択 'しないで、一気にそれを行います。 – chris85

+0

トランザクションでは同じ番号を選択するのに十分ではありませんが、キーが一意の場合は2番目の挿入が失敗し、トランザクションを取り消してからもう一度やり直すことができます。 – awiebe

答えて

0

これで十分である:

まず、テーブルを変更します。 No id,date is a DATE`、 "natural" PK。

number INT(6) unsigned zerofill, 
date DATE, 
PRIMARY KEY(date, number) 

その後のトランザクションが

BEGIN; 
($not_first, $number) = SELECT date = CURDATE(), number 
        FROM tbl 
        ORDER BY date DESC, number DESC 
        LIMIT 1 
        FOR UPDATE; -- important 
$number = $not_first ? $number+1 : 1; 
INSERT INTO tbl (date, number, ...) 
    VALUES 
    (CURDATE(), $number, ...); 
COMMIT; 

あるしかしちょうど真夜中の前にSELECT実行されますが、INSERT実行直後ならばバグがまだあります。

ので、これは、ステートメントがアトミックでなければなりませんから、バグを回避し、トランザクションを回避することができる:

INSERT INTO tbl (date, number, ...) 
    VALUES 
    (CURDATE(), 
     IFNULL((SELECT MAX(id) FROM tbl WHERE date = CURDATE()), 
        1) AS number, 
     ... -- whatever else you are inserting into `tbl` 
    ); 

にかかわらず、あなたがそれを行う方法の、あなたがエラーをチェックしなければならない - 別の接続がやっている可能性がありデッドロックや何かにつながる同じ事。

関連する問題