【问题标题】:Foreign key constraint should be complaining but somehow it's not外键约束应该抱怨,但不知何故不是
【发布时间】:2011-06-25 15:26:30
【问题描述】:

除其他外,我有三个表:accountaddressaccount_addressaccount_address 表有一个account_id 和一个address_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 在根本上行为不端

我当然希望 #1 是这样,但我不知道我可能会误解什么。这对我来说是一个最高级别的谜。任何想法将不胜感激。

【问题讨论】:

  • 重要提示:我从来没有禁用外键检查,而且我是唯一一个接触过这个数据库的人。
  • 没有帮助,但仅供参考:“无操作”等同于 mysql 中的“限制”(正如您所暗示的,所以说明显而易见,但仍然:))
  • 另外两个表也是 InnoDB 吗?
  • 是的,我所有的表都是 InnoDB。

标签: mysql foreign-keys database-integrity


【解决方案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;

)

【讨论】:

  • 这是个好主意,但我不认为我的情况如此。我绝对从未在我的数据库上禁用外键检查。 (这违背了我的数据库信仰。)另外,当我尝试在不是所有约束“签出”的表上设置外键约束时,它不会让我添加约束。我很确定这意味着当我添加约束时,所有外键都已检出,从那时起,它不应该让键失控。但他们不合时宜,完全不合时宜。
【解决方案2】:

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

您丢失的记录可能在禁用密钥检查期间被删除。要测试密钥现在是否正常工作,请添加几条相关记录并删除一条,由于 FK 上的“无操作”设置,这应该会失败。如果它继续,那么要么您不在 InnoDB 上(可能它已被禁用并且 mysql 正在默默地转换为 MyISAM),密钥检查已关闭(检查该服务器变量),或者您的服务器确实有问题。

【讨论】:

  • 这里有一些不错的想法,但是,再一次,这些都不是我的情况。我从来没有禁用外键检查。刚才尝试插入一条account_address 记录,其中address_id 好,account_id 不好。它没有让我插入,因为 account_id 外键失败。此外,account_addressaccount 都在 InnoDB 上。
  • 另外,我刚刚检查了我的服务器变量和外键检查。
猜你喜欢
  • 2017-09-03
  • 2015-12-23
  • 1970-01-01
  • 1970-01-01
  • 2013-01-03
  • 2015-12-07
  • 2020-03-07
  • 1970-01-01
  • 2014-05-12
相关资源
最近更新 更多