我正在编写一个查询,以防止并发查询搞砸了。 我很确定这是一个更好的方法,这就是我在这里的原因。
基本上表格的列如下所示:
id : INT(10) unsigned auto_incremented
number : INT(6) unsigned zerofill
date : DATETIME
当我插入新行时,我需要设置数字值:
我想到首先选择最后一行的日期和数字,使用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 :(得分:0)
这可能就足够了:
首先,更改表格。没有id
,date is a
DATE`,&#34;自然&#34; 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`
);
无论你如何做,你都必须检查错误 - 另一个连接可能会做同样的事情并导致死锁等。