PHP & MySQL: 如何创建唯一的“年-月-数”键?

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

我必须为发票创建唯一 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 并且我有以下 table

本书

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; returns 列号因为它是 auto increment columnLAST_INSERT_ID(number+1) 增加了最后插入的 number (也可能是不同的用户?我不确定,我很难在文档中找到它 http://dev.mysql.com/doc/refman/5.7/en/information-functions.html#function_last-insert-id)

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

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

类似这样的问题,您可以尝试一下——打开两个终端windows并使用mysql客户端。

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

在两个并发会话中启动事务:

mysql1> begin;

mysql2> begin;

会话 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 |
+------+-------+--------+

会话 2 仍然看到原始数值,因为可重复读取事务隔离。但是一旦它尝试执行它自己的增量,它就会等待,因为会话 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

在会话 1 中提交:

mysql1> commit;

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

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