Entity Framework与SQL Server:外键约束疑似违规问题咨询
这事儿确实有点反常——你明明给表B的ForeignKeyId设置了不可为空的外键约束,理论上B里的每一条记录都必须能在表A中找到对应的PrimaryKeyId才对。但你的查询:
SELECT B.ID, B.ForeignKeyId, A.PrimaryKeyId FROM B LEFT OUTER JOIN A ON A.PrimaryKeyId = B.ForeignKeyId WHERE A.PrimaryKeyId IS NULL
返回了这样的结果:
B.ID B.ForeignKeyId A.PrimaryKeyId
11 21 NULL
12 22 NULL
这说明确实存在B表外键在A表无匹配的情况,下面是几个最常见的原因和对应的解决思路:
外键约束压根没生效
别不信,这种情况真的很常见:可能你创建表时写错了语句,或者数据库引擎不支持外键(比如MySQL的MyISAM引擎),甚至约束后来被误删了。你可以用对应数据库的命令确认约束是否存在:- MySQL: 执行
SHOW CREATE TABLE B;看输出里有没有外键相关的定义 - PostgreSQL: 用
SELECT conname FROM pg_constraint WHERE conrelid = 'B'::regclass AND contype = 'f';查看B表的外键约束 - SQL Server: 运行
EXEC sp_helpconstraint 'B';检查约束列表
- MySQL: 执行
脏数据是在约束创建前插进去的
如果表B在添加外键约束之前就已经有了这些不符合规则的数据,有些数据库在创建约束时如果没加强制校验的选项(比如SQL Server的WITH CHECK、PostgreSQL的VALIDATE),会默认允许历史脏数据保留,只限制后续的插入/更新。这种情况你得先清理掉这些脏数据:-- 先备份好数据!然后删除不符合约束的记录 DELETE FROM B WHERE ForeignKeyId NOT IN (SELECT PrimaryKeyId FROM A); -- 要是有合适的替代值,也可以更新成合法的外键 UPDATE B SET ForeignKeyId = <合法的PrimaryKeyId> WHERE ForeignKeyId NOT IN (SELECT PrimaryKeyId FROM A);并发事务导致的临时不一致
如果查询的同时,刚好有其他事务在删除A表的记录,而你的查询用了较低的隔离级别(比如READ UNCOMMITTED),就可能看到这种暂时的不一致。你可以试试在事务里用更高的隔离级别(比如REPEATABLE READ)重新查询,或者确认这段时间有没有并发的删除操作在执行。字段数据类型不匹配
有时候看起来字段名对应,但A.PrimaryKeyId和B.ForeignKeyId的数据类型可能暗藏差异——比如一个是INT,一个是BIGINT;或者字符串类型的字符编码/排序规则不同,导致匹配失败。去检查一下两个字段的定义是否完全一致。
解决完问题后,记得确认外键约束是启用且正常生效的,避免后续再出现类似的脏数据问题。
内容的提问来源于stack exchange,提问作者vpetrenii

