2009-04-11 45 views
0

这是我当前数据库的摘录(改表名字更容易理解):作用域/复合代理键在MySQL

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

哪里id始终是一个代理键,用auto_increment创建。

我希望代理键Pet.id被作为“作用域”的Pet.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的交易/ transactionvars工作,所以我不知道会不会有这一个严重的问题。 注意:我不想重复使用曾经给宠物一次的id。这就是为什么我不使用MAX()此解决方案是否有任何警告?

回答

1

我不这么认为。如果您真的必须拥有该模式,您可以使用事务来选择MAX(id)WHERE ownerFK,然后选择INSERT。

我非常怀疑这个模式有一个很好的理由,主键现在也是关键的事实,这可能会使数据库理论家不高兴。

通常情况下,你会希望'id'真的是一个真正的主键,ownerFK用于分组,如果你需要它,还有一个单独的'等级'列,以每个拥有者的特定顺序,以及一个UNIQUE索引(ownerFK,rank)。

+0

我明白了你的观点。我的实际数据库设计有点不同,我也不需要它成为主键的一部分。我用目前正在评估的解决方案更新了这个问题。 – 2009-04-11 16:34:48