I am writing a query in order to prevent concurrent queries to mess things up. I'm pretty sure there's a much better way to do it, and that's why I'm here.
Basically the columns of the table look like this:
id : INT(10) unsigned auto_incremented
number : INT(6) unsigned zerofill
date : DATETIME
When I insert a new row, here is how I need to set its number value :
- If the last inserted row's date is today, then number = last inserted row's number + 1
- If the last inserted row's date is yesterday or before, then number = 1
I thought about first select the last row's date and number, set the new number using php and then make my insert.
$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();
Are transactions enough to prevent another thread selecting the same number and therefor trying to insert the same number I am trying to ?
Since I couldn't answer that question myself, even using our dear friend Google and SO, I thought "Can't I just have MySQL do all the work ?" and came up with this query :
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()
Is it a better way to do it ?
Should I just lock the table before doing anything ?
Am I just too dumb to see how this should be done ?