【发布时间】:2014-12-30 13:49:48
【问题描述】:
我知道,mysql 支持自动增量值,但不支持 依赖 自动增量值。
即如果你有这样的表:
id | element | innerId
1 | a | 1
2 | a | 2
3 | b | 1
而你插入另一个b-element,你需要计算你自己的innerId,(预计插入为“2”)
- 是否有数据库支持这样的功能?
实现这种行为的最佳方法是什么?我不知道元素的数量,所以我无法为它们创建 dedicated 表,我只能在其中导出一个 id。
(例子很简单)
应该实现的目标是任何元素“类型”(如果数字未知,possibly infitine -1 应该有它自己的、无间隙的 id。
如果我会使用类似的东西
INSERT INTO
myTable t1
(id,element, innerId)
VALUES
(null, 'b', (SELECT COUNT(*) FROM myTable t2 WHERE t2.element = "b") +1)
http://sqlfiddle.com/#!2/2f4543/1
这会在所有情况下返回预期结果吗?我的意思是它可以工作,但是并发呢?带有 SubSelects 的插入仍然是 atomic 还是可能存在一个场景,两个插入将尝试插入相同的 id? (尤其是在事务插入待处理的情况下?)
尝试使用编程语言(即 Java)实现这一点会更好吗?还是更容易在尽可能接近数据库引擎的地方实现这个逻辑?
由于我使用聚合来计算next innerId,我认为使用SELECT...FOR UPDATE 无法避免其他事务有待提交的问题,对吧?
ps.:我可以。只是 bruteforce 插入 - 从每个元素的当前最大值开始 - 在 (element,innerId) 上具有唯一键约束,直到没有foreignKey-violation - 但没有一个 更好的 em> 方法?
根据Make one ID with auto_increment depending on another ID - possible?,可以使用复合主键 - 在我的情况下 - innerId and element。但根据这个仅适用于 MyIsam 的setting MySQL auto_increment to be dependent on two other primary keys(我有 InnoDB)
现在我更加困惑了。我尝试使用 2 个不同的 php 脚本插入数据,使用上面的查询。虽然脚本一“休眠”了 15 秒,以允许我调用脚本二(它应该模拟并发修改) - 结果是正确的使用一个查询时。
(ps.:mysql(?!i)-仅用于快速调试的功能)
基础数据:
脚本 1:
mysql_query("START TRANSACTION");
mysql_query("INSERT INTO insertTest (id, element, innerId, fromPage)VALUES(null, 'a', (SELECT MAX(t2.innerID) FROM insertTest t2 WHERE element='a') +1, 'page1')");
sleep(15);
//mysql_query("ROLLBACK;");
mysql_query("COMMIT;");
脚本 2:
//mysql_query("START TRANSACTION");
mysql_query("INSERT INTO insertTest (id, element, innerId, fromPage)VALUES(null, 'a', (SELECT MAX(t2.innerID) FROM insertTest t2 WHERE element='a') +1, 'page2')");
//mysql_query("COMMIT;");
我原以为page2 插入会在page1 插入之前发生,因为它在没有任何事务的情况下运行。但实际上page1的插入是FIRST发生的,导致第二个脚本也延迟了15秒左右……
(忽略AC-Id,玩了一下)
在第一个脚本上使用Rollback 时,第二个脚本仍然延迟 15 秒,然后然后选择正确的innerId:
所以:
- 事务处于活动状态时,非事务性插入被阻止。
- 带有子选择的插入似乎也被阻止了。
- 所以最后看起来带有子选择的插入是原子操作?不然为什么第二页的
SELECT被屏蔽了?
像这样在单独的非事务性语句中使用选择和插入(第 2 页,模拟并发修改):
$nextId = mysql_query("SELECT MAX(t2.innerID) as a FROM insertTest t2 WHERE element='a'");
$nextId = mysql_fetch_array($nextId);
$nextId = $nextId["a"] +1;
mysql_query("INSERT INTO insertTest (id, element, innerId, fromPage)VALUES(null, 'a', $nextId, 'page2')");
导致我试图避免的错误:
那么当每次修改都是一个查询时,为什么它在并发场景中起作用? 带有子选择的插入是原子的吗?
【问题讨论】:
标签: java mysql transactions atomic