PHP & MySQL : How to create unique `year-month-number` keys?

标签 php mysql innodb auto-increment

我必须为发票创建唯一 ID。每个发票 ID 的格式都是 year-month-number,它由发票的当前年份和月份以及一个从 1 开始的递增数字组成(不允许有间隙)。

例如,如果我们在 2017 年 1 月有 4 张发票,我将有以下 4 个发票 ID:

2017-1-1
2017-1-2
2017-1-3
2017-1-4

现在我想创建一个应用来创建这些唯一 ID。特别是我想确保即使 2 个人同时请求发票编号,他们也应该获得不同的 ID。

我正在使用 InnoDB 并且我有下表

year  | month  | number | 
------------------------
2017  | 7      | 2      | 
2017  | 6      | 5      |
2017  | 5      | 6      |

如果尚未为 year-month 对创建发票,则数据库中没有条目。主键是 year-month 对,numberauto increment index

假设我会这样计算下一个发票 ID:

$stmt = $db->prepare('INSERT INTO book(year,month,number) 
                      VALUES (?,?,1) 
                      ON DUPLICATE KEY UPDATE number= LAST_INSERT_ID(number+1)');
$stmt->bind_param('ii', $year, $month);
$stmt->execute();
echo 'Next invoice id: ' . $year . '-' . $month . - . $db->insert_id;

说明:$db->insert_id; 返回列号,因为它是一个自动递增列并且LAST_INSERT_ID(number+1)递增最后插入的 number (也可能是由不同的用户插入的?我不确定,我很难在文档 http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id 中找到它)

这段代码真的有效吗,或者如果人们同时执行这段代码,它是否会创建多个相同的 ID?

编辑 假设当前月/年的数量为 5。 为什么不可能 2 个人同时计算发票,以便两个查询同时将数字 5 升级为 6?在这种情况下,他们都会得到发票 ID“2017-11-6”吧?

最佳答案

对于这样的问题,您可以尝试一下——打开两个终端窗口并使用 mysql 客户端。

mysql1> select * from book;
+------+-------+--------+
| year | month | number |
+------+-------+--------+
| 2017 |     5 |      5 |
+------+-------+--------+

在两个并发 session 中启动事务:

mysql1> begin;

mysql2> begin;

session 1 执行 IODKU 并递增数字(但尚未提交,因为 begin 隐式地使我们退出自动提交模式):

mysql11> insert into book values (2017, 5, 0) 
  on duplicate key update number = last_insert_id(number+1);

mysql1> select * from book;
+------+-------+--------+
| year | month | number |
+------+-------+--------+
| 2017 |     5 |      6 |
+------+-------+--------+

session 2 仍然看到原始数值,因为可重复读取事务隔离。但是一旦它尝试执行它自己的增量,它就会等待,因为 session 1 仍然锁定该行。

mysql2> select * from book;
+------+-------+--------+
| year | month | number |
+------+-------+--------+
| 2017 |     5 |      5 |
+------+-------+--------+
mysql12> insert into book values (2017, 5, 0) 
  on duplicate key update number = last_insert_id(number+1);
-- waits for lock

在 session 1 中提交:

mysql1> commit;

现在 session 2 中的 IODKU 完成了,我们可以看到它第二次增加了数字:

mysql2> select * from book;
+------+-------+--------+
| year | month | number |
+------+-------+--------+
| 2017 |     5 |      7 |
+------+-------+--------+

关于PHP & MySQL : How to create unique `year-month-number` keys?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47460729/

相关文章:

php - 小于号的MySQL时间戳比较

mysql - 无法将列添加到表中 - mysql

mysql 查询两个表?

email - 警告 : mail(mail. 日志):无法打开流:权限被拒绝

php - 尝试使用 twilio Api phpSDK 发送消息时发生 fatal error

php - 在 'Google\Protobuf\Internal\Message' 中找不到类 "Message.proto"

mysql - Yii 框架——InnoDB 与 MyISAM

MySQL SQL_NO_CACHE 不工作

php - InnoDB表中MySQL全文的替换相关性排序?

php - Symfony2 : how to check if an action is secured?