外键约束应该抱怨,但不知何故它不是
我有三个表:account
、address
和 account_address
。 account_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
看到了吗? 外键 (account_id) 引用帐户 (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 不应该在某个时候告诉我外键约束失败吗?!
我只看到两种可能性:
- 我误解了我所看到的内容
- 我的 DBMS 从根本上来说行为不当 我当然
希望#1 是这样,但我不知道我可能会误解什么。这对我来说是一个最高级别的谜。任何想法将不胜感激。
I have, among others, three tables: account
, address
and account_address
. The account_address
table has an account_id
and an address_id
. It's your standard many-to-many relationship.
I have a perplexing situation where I have an account_address
record that points to an account
that doesn't exist. Since I have a foreign key on account_address.account_id
pointing to account
, this shouldn't be able to happen, right?
Now let me prove that this should-be-impossible thing is happening. First I'll show you my table definition:
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
See? FOREIGN KEY (account_id) REFERENCES account (id)
.
Now here are a few queries that show that constraint failing:
select aa.account_id
from account_address aa
where aa.address_id = 15116
That query gives me the following result:
15116
37033
62325
71857
93774
119066
So apparently address 15116 is attached to six different accounts (one account, interestingly having the same id as the address). But check this out:
select * from account where id in (15116, 37033, 62325, 71857, 93774, 119066)
NO results! Shouldn't my DBMS have told me at some point that I have a foreign key constraint failing?!
I see only two possibilities:
- I'm misinterpreting what I'm seeing
- My DBMS is misbehaving in a fundamental way
I sure hope #1 is the case but I don't know what I could possibly be misinterpreting. It's a mystery to me of the highest order. Any thoughts would be greatly appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
约束将阻止任何“邪恶”行为,但不会追溯确保一切正常。正如许多导入脚本根据事情发生的顺序所做的那样,您可以将这些约束的检查设置为 0。
因此,如果由于某种原因信息不正确,则可能会发生这种情况。那么您的 DBMS 就不会行为不当,您也不会产生误解。
所以我会选择选项3:某些导入或插入行为不当,可能使用“setforeign_key_checks = 0”。或者是旧数据。
(来自手册
:)
A constraint will stop any actions to do something 'evil', but will not retroactivly make sure everything is allright. You can, as many import scripts do because of the order in which stuff happens, set the checking of these constraints to 0.
So if for some reason the information is incorrect, this situation can occur. Then your DBMS is not misbehaving, and you're also not misinterpreting.
So i'd go for option 3: Some import or insert is misbehaving, possibly using "set foreign_key_checks = 0". Or it is old data.
(from the manual:
)
MySQL 确实有一个服务器变量来禁用外键检查 -
setforeign_key_checks=0
,它用于导入转储文件等情况,其中表可能有一个 FK 指向“稍后”的表。尚未加载的转储。通常这会终止导入,即使数据很好。禁用 FK 检查允许继续导入。您丢失的记录可能是在禁用密钥检查期间被删除的。要测试密钥现在是否正常工作,请添加几个相关记录并删除一个,由于 FK 上的“无操作”设置,这应该会失败。如果继续,那么要么你不在 InnoDB 上(也许它被禁用并且 mysql 默默地转换为 MyISAM),关键检查被关闭(检查服务器变量),或者你的服务器确实有问题。
MySQL does have a server variable to disable foreign key checks -
set foreign_key_checks=0
, which is used in cases like importing a dump file where a table may have an FK pointing at a table "later" in the dump that hasn't been loaded yet. Normally this would kill the import, even though the data's fine. Disabling the FK checks allows the import to proceed.It's possible that your missing records were deleted during a time when the key check was disabled. To test if the keys are working correctly now, add a couple related records and delete one, which should fail due to the "no action" setting on the FK. If it proceeds, then either you're not on InnoDB (maybe it's disabled and mysql's silently transforming to MyISAM), the key checks are turned off (check that server variable), or something's really screwy with your server.