MySQL 将特定 month/year 组合的值更新为唯一
MySQL Update value to unique for specific month/year combination
我有table记录包含:id、年、月、数、值
创建记录时 number 设置为 NULL。
稍后注册记录时,我需要为该记录创建唯一的 number,这对于特定的 year 和 month 组合,应该从本月存在的最后一个 number 开始递增。与此同时,其他人可能会尝试这样做,并且很可能会选择与我相同的递增数字。如果它们是同时创建的,我需要避免重复。
如果给定月份没有编号,则应从 1 开始。
数字值可能会在 table 中重复,但对于相同的 year/month 组合则不会。
我想这样做:
- 锁定 table
- 对现有值进行降序排序(对于给定 month/year)并获得第一个
- 增加它并更新特定记录
- 解锁table
但我想知道是否有更干净优雅的方法来解决它?我想避免锁定 table 太久,或者在应用端出现问题时冒着被锁定的风险。
此致。
要求的测试数据:
CREATE TABLE RECORDS (`id` int(11) NOT NULL AUTO_INCREMENT, `year` int(4), `month` int(4), `number` int(4), `value` int(4), PRIMARY KEY (`id`));
INSERT INTO RECORDS (year, month, number, value) VALUES
(2019, 10, 1, 100),
(2019, 10, 2, 200),
(2019, 10, 5, 300),
(2019, 10, 3, 400),
(2019, 10, 4, 500),
(2019, 10, 6, 600),
(2019, 10, NULL, 700),
(2019, 10, NULL, 800),
(2019, 10, NULL, 900),
(2019, 11, 1, 100),
(2019, 11, NULL, 200),
(2019, 11, NULL, 300),
(2019, 11, 2, 400),
(2019, 11, 3, 500),
(2019, 11, NULL, 600),
(2019, 11, 4, 700),
(2019, 12, 1, 100),
(2019, 12, 2, 300),
(2019, 12, NULL, 200),
(2019, 12, NULL, 500),
(2019, 12, NULL, 600),
(2019, 12, NULL, 700),
(2019, 12, NULL, 800);
Fiddle:
https://www.db-fiddle.com/f/d4Dfs5zcBUU1X95TaaHbNx/0#&togetherjs=93fntrtw0y
我相信这些是根据您的需要最有效地更新的基本步骤。
- 在
year
、month
、number
上创建唯一索引(按此顺序)以防止重复并允许过滤锁定。
- 在递增数字时使用
FOR UPDATE
以防止其他事务在更新期间读取行,同时仅锁定具有相应年份和月份的行,而不是整个table.
BEGIN;
SELECT MAX(NUMBER)
FROM RECORDS
WHERE YEAR = ? AND MONTH = ?
FOR UPDATE;
UPDATE RECORDS
SET NUMBER = ?
WHERE YEAR = ? AND MONTH = ?;
COMMIT;
我有table记录包含:id、年、月、数、值
创建记录时 number 设置为 NULL。
稍后注册记录时,我需要为该记录创建唯一的 number,这对于特定的 year 和 month 组合,应该从本月存在的最后一个 number 开始递增。与此同时,其他人可能会尝试这样做,并且很可能会选择与我相同的递增数字。如果它们是同时创建的,我需要避免重复。
如果给定月份没有编号,则应从 1 开始。
数字值可能会在 table 中重复,但对于相同的 year/month 组合则不会。
我想这样做:
- 锁定 table
- 对现有值进行降序排序(对于给定 month/year)并获得第一个
- 增加它并更新特定记录
- 解锁table
但我想知道是否有更干净优雅的方法来解决它?我想避免锁定 table 太久,或者在应用端出现问题时冒着被锁定的风险。
此致。
要求的测试数据:
CREATE TABLE RECORDS (`id` int(11) NOT NULL AUTO_INCREMENT, `year` int(4), `month` int(4), `number` int(4), `value` int(4), PRIMARY KEY (`id`));
INSERT INTO RECORDS (year, month, number, value) VALUES
(2019, 10, 1, 100),
(2019, 10, 2, 200),
(2019, 10, 5, 300),
(2019, 10, 3, 400),
(2019, 10, 4, 500),
(2019, 10, 6, 600),
(2019, 10, NULL, 700),
(2019, 10, NULL, 800),
(2019, 10, NULL, 900),
(2019, 11, 1, 100),
(2019, 11, NULL, 200),
(2019, 11, NULL, 300),
(2019, 11, 2, 400),
(2019, 11, 3, 500),
(2019, 11, NULL, 600),
(2019, 11, 4, 700),
(2019, 12, 1, 100),
(2019, 12, 2, 300),
(2019, 12, NULL, 200),
(2019, 12, NULL, 500),
(2019, 12, NULL, 600),
(2019, 12, NULL, 700),
(2019, 12, NULL, 800);
Fiddle: https://www.db-fiddle.com/f/d4Dfs5zcBUU1X95TaaHbNx/0#&togetherjs=93fntrtw0y
我相信这些是根据您的需要最有效地更新的基本步骤。
- 在
year
、month
、number
上创建唯一索引(按此顺序)以防止重复并允许过滤锁定。 - 在递增数字时使用
FOR UPDATE
以防止其他事务在更新期间读取行,同时仅锁定具有相应年份和月份的行,而不是整个table.
BEGIN;
SELECT MAX(NUMBER)
FROM RECORDS
WHERE YEAR = ? AND MONTH = ?
FOR UPDATE;
UPDATE RECORDS
SET NUMBER = ?
WHERE YEAR = ? AND MONTH = ?;
COMMIT;