我正在使用MySQL的AUTO_INCREMENT字段和InnoDB支持事务。我回滚事务时注意到,AUTO_INCREMENT字段未回滚吗?我发现它是按这种方式设计的,但是有没有解决方法?
Answers:
让我指出一些非常重要的事情:
也就是说,除了将它们的相等性(=)或不相等性(<>)进行比较之外,您不应做任何其他事情。没有关系运算符(<,>),没有按索引排序,等等。如果需要按“添加日期”排序,请在“添加日期”列中添加。
将它们当作苹果和橙子:问一个苹果是否和橙子一样有意义吗?是。询问苹果是否大于橙子是否有意义?不。(实际上,确实可以,但是您明白我的意思。)
如果您遵守此规则,则自动生成索引的连续性差异不会引起问题。
它不能那样工作。考虑:
数据库如何回收557值?它进入FOO并递减所有其他大于557的主键吗?如何修复BAR?如何清除打印在报告程序三输出上的558?
出于相同的原因,Oracle的序列号也与事务无关。
如果您可以在固定时间内解决此问题,那么我相信您可以在数据库领域赚很多钱。
现在,如果您有一个要求,您的自动递增字段必须没有空格(例如,出于审计目的)。这样便无法回滚您的交易。相反,您需要在记录上有一个状态标志。第一次插入时,记录的状态为“未完成”,然后开始事务,进行工作并将状态更新为“竞争”(或任何需要的状态)。然后,当您提交时,记录处于活动状态。如果事务回滚,则不完整的记录仍然存在以供审核。这将引起您很多其他麻烦,但这是处理审计跟踪的一种方法。
我有一个客户需要ID才能在发票表上回滚,该订单必须是连续的
我在MySQL中的解决方案是删除AUTO-INCREMENT并从表中获取最新ID,添加一个(+1),然后手动将其插入。
如果表名为“ TableA”,并且“自动增量”列为“ Id”
INSERT INTO TableA (Id, Col2, Col3, Col4, ...)
VALUES (
(SELECT Id FROM TableA t ORDER BY t.Id DESC LIMIT 1)+1,
Col2_Val, Col3_Val, Col4_Val, ...)
(SELECT MAX(id) AS maxval FROM table_name;)+1
如果在回滚或删除后设置auto_increment
为1
,则在下一次插入时,MySQL将看到1
已使用该MAX()
值,而是获取该值并将其加1。
这将确保如果删除最后一个值的行(或回滚插入),则将重用该行。
要将auto_increment设置为1,请执行以下操作:
ALTER TABLE tbl auto_increment = 1
这不像继续下一个数字那样有效,因为 MAX()
可能会很昂贵,但是如果您不经常删除/回滚并且沉迷于重用最高值,那么这是一种现实的方法。
请注意,这不能防止中间删除的记录出现间隙,或者在将auto_increment设置回1之前是否应该进行另一次插入。
ANALYZE TABLE tbl
在之后调用,否则旧的AUTO_INCREMENT值仍将显示在information_schema.TABLES中,直到过期。
针对这个特定难题(我也有)的具体答案如下:
1)创建一个表,其中包含用于不同凭证(发票,收据,RMA等)的不同计数器;为每个文档插入一条记录,并将初始计数器添加为0。
2)在创建新文档之前,请执行以下操作(例如,针对发票):
UPDATE document_counters SET counter = LAST_INSERT_ID(counter + 1) where type = 'invoice'
3)获取您刚刚更新到的最后一个值,如下所示:
SELECT LAST_INSERT_ID()
或仅使用您的PHP(或任何其他方式)mysql_insert_id()函数来获得相同的结果
4)插入新记录以及刚从数据库中获得的主要ID。这将覆盖当前的自动增量索引,并确保记录之间没有ID间隔。
当然,这整个过程都需要包装在事务中。此方法的优点在于,当您回滚事务时,步骤2中的UPDATE语句将被回滚,并且计数器将不再更改。其他并发事务将阻塞,直到第一个事务被提交或回滚,这样它们将无法访问旧计数器或新计数器,直到所有其他事务都首先完成。
解:
让我们以“ tbl_test”作为示例表,并假设字段“ Id”具有AUTO_INCREMENT属性
CREATE TABLE tbl_test (
Id int NOT NULL AUTO_INCREMENT ,
Name varchar(255) NULL ,
PRIMARY KEY (`Id`)
)
;
让我们假设该表已陷入困境或已经插入了数千行,并且您不想再使用AUTO_INCREMENT了;因为当您回滚事务时,字段“ Id”总是向AUTO_INCREMENT值添加+1。因此,为了避免这种情况,您可以执行以下操作:
ALTER TABLE tbl_test修改列ID int(11)不为空;
创建触发器trg_tbl_test_1 在tbl_test上插入之前 每行 开始 SET NEW.Id = COALESCE((从tbl_test中选择MAX(Id)),0)+ 1; 结束;
而已!你完成了!
别客气。
如果您需要按编号顺序无间隙地分配ID,则不能使用自动增量列。您需要定义一个标准的整数列,并使用一个存储过程来计算插入序列中的下一个数字,并将记录插入事务中。如果插入失败,则下次调用该过程时,它将重新计算下一个ID。
话虽这么说,但要让id保持特定顺序且没有间隙是一个坏主意。如果需要保留顺序,则可能应在插入时(并可能在更新时)标记行的时间戳。
$masterConn = mysql_connect("localhost", "root", '');
mysql_select_db("sample", $masterConn);
for($i=1; $i<=10; $i++) {
mysql_query("START TRANSACTION",$masterConn);
$qry_insert = "INSERT INTO `customer` (id, `a`, `b`) VALUES (NULL, '$i', 'a')";
mysql_query($qry_insert,$masterConn);
if($i%2==1) mysql_query("COMMIT",$masterConn);
else mysql_query("ROLLBACK",$masterConn);
mysql_query("ALTER TABLE customer auto_increment = 1",$masterConn);
}
echo "Done";