MySQL中的范围/复合代理键

时间:2009-04-11 14:40:48

标签: mysql transactions innodb auto-increment composite

以下是我当前数据库的摘录(更改了表名以便于理解):

Pet(ownerFK, id, name, age)
Owner(id, name)

id始终是使用auto_increment创建的代理键。

我希望代理键Pet.idPet.ownerFK“限定”,或者换句话说,将复合键[ownerFk, id]作为我的最小键。我希望表格的行为如下:

INSERT Pet(1, ?, "Garfield", 8);
INSERT Pet(1, ?, "Pluto", 12);
INSERT Pet(2, ?, "Mortimer", 1);

SELECT * FROM Pet;
  RESULT:
   Pet(1, 1, "Garfield", 8)
   Pet(1, 2, "Pluto", 12)
   Pet(2, 1, "Mortimer", 1)

我目前正在使用此feature of MyISAM,其中“您可以在多列索引中的辅助列上指定AUTO_INCREMENT。在这种情况下,AUTO_INCREMENT列的生成值为计算为MAX(auto_increment_column) + 1 WHERE prefix=given-prefix。当您想要将数据放入有序组时,这非常有用。“

然而,由于各种(可能是显而易见的)原因,我想从MyISAM切换到InnoDB,因为我需要在某些地方进行交易。

有没有办法如何用 InnoDB 实现这种效果?

我在这个问题上发现了一些帖子,他们中的许多人建议在插入之前对表进行写锁定。我对此并不是很熟悉,但对于这个问题,我不会对表格进行一点点改动吗?如果可能的话,我宁愿考虑写安全事务(我之前从未做过) - 将Owner.current_pet_counter作为辅助字段。

所以另一个可接受的解决方案是......

实际上我不需要“范围”ID作为实际密钥的一部分。我的实际数据库设计使用了一个单独的“永久链接”表,该表使用了这个“功能”。我目前使用它作为缺少事务的解决方法。我想到了以下替代方案:

 Pet(id, ownerFK, scopedId, name, age), KEY(id), UNIQUE(ownerFK, scopedId)
 Owner(id, name, current_pet_counter)

 START TRANSACTION WITH CONSISTENT SNAPSHOT;
 SELECT @new=current_pet_counter FROM Owner WHERE id = :owner_id;
 INSERT Pet(?, :owner_id, @new, "Pluto", 21);
 UPDATE Owners SET current_pet_counter = @new + 1 WHERE id = :owner_id;
 COMMIT;

我还没有使用MySQL中的transaction / transactionvars,所以我不知道这个问题是否存在严重问题。 注意:我不想重复使用曾经给宠物一次的id。这就是我不使用MAX()的原因。 此解决方案是否有任何警告?

1 个答案:

答案 0 :(得分:1)

我不相信。如果你真的必须拥有那个模式,你可以使用一个事务来选择MAX(id)WHERE ownerFK,然后INSERT。

我非常怀疑这种架构是否有充分的理由;主键现在也是关于密钥的事实,这可能会使数据库理论家感到不快。

通常你想让'id'真正成为一个合适的主键,ownerFK用于分组,如果你需要的话,还有一个单独的'rank'列,可以按照每个拥有者的特定顺序放置宠物,和(ownerFK,rank)上的UNIQUE索引。