Tsql ADO.net SQL Server 2012-具有复合键且缺少SchemaAction.AddWithKey的查询不正确地添加了约束

Tsql ADO.net SQL Server 2012-具有复合键且缺少SchemaAction.AddWithKey的查询不正确地添加了约束,tsql,ado.net,sql-server-2012,Tsql,Ado.net,Sql Server 2012,当使用MissingSchemaAction.AddWithKey在ADO.net上运行此查询时,会引发异常: 未能启用约束。一个或多个行包含违反非空、唯一或外键约束的值 查询: 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 检查局部变量会发现P

当使用MissingSchemaAction.AddWithKey在ADO.net上运行此查询时,会引发异常:

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

查询:

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 
检查局部变量会发现PersonId的唯一约束已添加。不仅如此,在SQL Server Manager中运行相同的查询将返回一个结果集,而不会出现任何警告或错误。这段代码正是在SQLServer2005上使用的。使用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

您可以在本文中找到一些有用的调试建议:

这就是说,我首先想到的是,你所有的加入和分组都是不必要的。你已经写信了

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 Person中选择除b.PersonId之外的任何字段。但是您从JOIN子句中知道,b.PersonId的值必须等于map.PersonId,因此没有来自GroupPersonMap中不存在的Person的信息。因此,我们可以去掉连接:

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——这是表中的所有字段。因此,您可以进一步简化,只需从GroupPersonMap中选择*,尽管在生产代码的实践中,最好总是枚举您想要的字段。确切地指定所需内容可以防止以后的架构更改


关于连接的另一个注意事项是:当您在主键上连接表时,理想情况下您希望在整个键上连接。这是著名的第三范式的第一阶。当一个表有一个复合键时,这意味着该键中的每个字段都包含JOIN和/或WHERE条件。否则,可能会导致半笛卡尔连接,或者,在这种情况下,可能会出现主键的另一半未指定的问题。我想,如果您想保持查询的原样,但仍要消除错误,您可以简单地说您感兴趣的组,例如WHERE map.GroupId=xxxx。如果你真的想要一个所有的列表,那么我想说一个简化的重写而不需要不必要的连接和分组将是你最好的第一条攻击线

此错误消息在什么时候出现?在第二次插入之后?