外键约束应该是抱怨,但不知何故不是

时间:2011-02-11 15:28:13

标签: mysql foreign-keys database-integrity

除其他外,我有三个表:accountaddressaccount_addressaccount_address表格包含account_idaddress_id。这是你标准的多对多关系。

我有一个令人困惑的情况,我有account_address记录,指向不存在的account。由于account_address.account_id上的外键指向account,因此不应该发生这种情况,对吧?

现在让我证明这应该是不可能的事情正在发生。首先,我将向您展示我的表格定义:

CREATE TABLE `account_address` (    
  `id` bigint(20) NOT NULL AUTO_INCREMENT,    
  `account_id` bigint(20) NOT NULL,    
  `address_id` bigint(20) NOT NULL,   
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',    
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',    
  PRIMARY KEY (`id`),    
  KEY `fk_account_address_account_id` (`account_id`),    
  KEY `fk_account_address_address_id` (`address_id`),   
  KEY `index_account_address_account_id` (`account_id`) USING BTREE,    
  KEY `index_account_address_address_id` (`address_id`) USING BTREE,    
  CONSTRAINT `fk_account_address_account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,    
  CONSTRAINT `fk_account_address_address_id` FOREIGN KEY (`address_id`) REFERENCES `address` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION    
) ENGINE=InnoDB AUTO_INCREMENT=697173 DEFAULT CHARSET=latin1

请参阅? FOREIGN KEY (account_id) REFERENCES account (id)

现在,这里有一些查询显示约束失败:

select aa.account_id
from account_address aa
where aa.address_id = 15116

该查询给出了以下结果:

15116
37033
62325
71857
93774
119066

显然,地址15116附加到六个不同的帐户(一个帐户,有趣的是与地址具有相同的ID)。但请查看:

select * from account where id in (15116, 37033, 62325, 71857, 93774, 119066)

没有结果! 我的DBMS不应该告诉我,我的外键约束失败了吗?!

我只看到两种可能性:

  1. 我误解了我所看到的内容
  2. 我的DBMS从根本上说是行为不端
  3. 我当然希望#1是这样的,但我不知道我可能会误解的是什么。对我来说,最高级别是一个谜。任何想法都将不胜感激。

2 个答案:

答案 0 :(得分:1)

约束会阻止任何行为做出“邪恶”,但不会追溯确保一切都是正确的。您可以像许多导入脚本那样,因为事件发生的顺序,将这些约束的检查设置为0。

因此,如果由于某种原因信息不正确,可能会出现这种情况。然后你的DBMS没有行为不端,你也没有误解。

所以我选择选项3 :某些导入或插入行为不正常,可能使用“set foreign_key_checks = 0”。或者它是旧数据。

(来自手册:

mysql> SET foreign_key_checks = 0;
mysql> SOURCE dump_file_name;
mysql> SET foreign_key_checks = 1;

答案 1 :(得分:0)

MySQL确实有一个服务器变量来禁用外键检查 - set foreign_key_checks=0,用于导入转储文件的情况,其中表可能有一个FK指向转储中的“稍后”的表尚未加载。通常这会导致导入失败,即使数据很好。禁用FK检查允许导入继续。

在禁用密钥检查期间,您的遗失记录可能会被删除。要测试密钥现在是否正常工作,请添加一些相关记录并删除一个,由于FK上的“无操作”设置,这些记录将失败。如果它继续,那么要么你不在InnoDB上(可能是它被禁用而且mysql默默地转换为MyISAM),关键检查被关闭(检查服务器变量),或者你的服务器真的搞砸了。