ADO.net SQL Server 2012 - 使用复合键进行查询,并且MissingSchemaAction.AddWithKey错误地添加了约束

Ein*_*jar 5 t-sql ado.net sql-server-2012

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

无法启用约束.一行或多行包含违反非null,唯一或外键约束的值.

查询:

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 
Run Code Online (Sandbox Code Playgroud)

检查本地人显示PersonId已添加了一个独特的约束.不仅如此,在SQL Server Manager中运行相同的查询还会返回结果集,而不会出现任何警告或错误.这个确切的代码用于在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])
Run Code Online (Sandbox Code Playgroud)

然后插入值:

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

INSERT INTO [Person]
SELECT 1
Run Code Online (Sandbox Code Playgroud)

Rob*_*t N 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 
Run Code Online (Sandbox Code Playgroud)

但你可以得到相同的结果

SELECT * FROM GroupPersonMap
Run Code Online (Sandbox Code Playgroud)

这就是我的意思:

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

SELECT map.GroupId, map.PersonId 
FROM [GroupPersonMap] as map
GROUP BY map.GroupId, map.PersonId 
Run Code Online (Sandbox Code Playgroud)

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

SELECT map.GroupId, map.PersonId 
FROM [GroupPersonMap] as map
Run Code Online (Sandbox Code Playgroud)

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

关于 JOIN 的另一个注意事项是:当您根据主键联接表时,理想情况下您希望联接整个键。(这是著名的“第三范式”的“第一顺序”。)当表具有复合键时,这意味着键中的每个字段都包含 JOIN 和/或 WHERE 标准。否则,您可能会遇到半笛卡尔连接,或者在这种情况下,可能会出现主键的另一半未指定的问题。我猜想,如果您想按原样保留查询,但仍然消除错误,您可以简单地通过说出您感兴趣的组来完成(例如 WHERE map.GroupId = xxxx)。如果您确实想要列出所有内容,那么我想说,没有不必要的加入和分组的简化重写将是您最好的第一线攻击。