5

此查询在 ADO.net 上运行时MissingSchemaAction.AddWithKey抛出异常:

无法启用约束。一行或多行包含违反非空、唯一或外键约束的值。

询问:

SELECT map.GroupId, b.PersonId 
FROM [GroupPersonMap] as map
INNER JOIN [Person] AS b ON b.PersonId = map.PersonId
GROUP BY map.GroupId, b.PersonId 

检查 locals 发现PersonId添加了一个唯一的约束。不仅如此,在 SQL Server 管理器中运行相同的查询会返回一个没有任何警告或错误的结果集。此确切代码用于 SQL Server 2005。使用 SQL Server 2005,在 ADO.net 上运行此查询时,查询会正确创建复合约束。这是升级问题吗?

作为旁注,我知道设置EnforceConstraints = false提供了一种解决方法。不过,理想情况下,我想从根本上解决这个问题。

设置重现:

CREATE TABLE [GroupPersonMap]
(
[GroupId] [int] NOT NULL,
[PersonId] [int] NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE [GroupPersonMap] ADD CONSTRAINT [PK_GroupPersonMAP] PRIMARY KEY CLUSTERED  ([GroupId], [PersonId])

CREATE TABLE [Person]
(
[PersonId] [int] NOT NULL IDENTITY(1, 1),
[Val] INT
) ON [PRIMARY]
GO
ALTER TABLE [Person] ADD CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED  ([PersonId])

然后插入值:

INSERT INTO [GroupPersonMap] 
SELECT 1, 1
UNION ALL
SELECT 2, 1

INSERT INTO [Person]
SELECT 1
4

1 回答 1

2

您可能会在这篇文章中找到一些有用的调试建议:

无法启用约束。一行或多行包含违反非空、唯一或外键约束的值

也就是说,在这里跳出来的第一件事是你所有的加入和分组都是不必要的。你写

SELECT map.GroupId, b.PersonId 
FROM [GroupPersonMap] as map
INNER JOIN [Person] AS b ON b.PersonId = map.PersonId
GROUP BY map.GroupId, b.PersonId 

但你可以得到相同的结果

SELECT * FROM GroupPersonMap

这就是我的意思:

1) 除了 b.PersonId 之外,您没有从表 b (Person) 中选择任何字段。但是您从 JOIN 子句中知道 b.PersonId 的值必须等于 map.PersonId,因此没有来自 GroupPersonMap 中的 Person 的信息。所以,我们可以去掉 JOIN:

SELECT map.GroupId, map.PersonId 
FROM [GroupPersonMap] as map
GROUP BY map.GroupId, map.PersonId 

2) 但是现在,map.GroupId, map.PersonId是该表的确切主键,所以我们知道不会发生实际的聚合——GroupId/PersonId 的每个组合根据定义都是唯一的。因此,每一行输入将“分组”到一个且只有一行输出。这意味着我们也可以去掉 GROUP BY 子句:

SELECT map.GroupId, map.PersonId 
FROM [GroupPersonMap] as map

3) 现在,我们在 SELECT 子句中剩下的就是map.GroupId, map.PersonId-- 这就是表中的所有字段。因此,您可以进一步简化为“SELECT * FROM GroupPersonMap”,但在实际生产代码中,最好始终枚举您想要的字段。(准确指定您想要的内容可以保护您免受以后的架构更改。)

关于您的 JOIN 的另一个注意事项:当您在其主键上加入表时,理想情况下您希望在整个键上加入。(这是著名的“第三范式”的“一阶”。)当表具有复合键时,这意味着在键中的每个字段都包含 JOIN 和/或 WHERE 条件。否则,您可能会遇到半笛卡尔连接,或者在这种情况下,可能会出现未指定另一半主键的问题。我猜想,如果你想保留你的查询,但仍然消除你的错误,你可以简单地通过说出你感兴趣的组来做到这一点(例如 WHERE map.GroupId = xxxx)。如果你真的想要所有的清单,那么我

于 2013-12-06T19:58:18.723 回答