【问题标题】:ADO.net SQL Server 2012 - Query with composite key and MissingSchemaAction.AddWithKey incorrectly adds constraintADO.net SQL Server 2012 - 使用复合键和 MissingSchemaAction.AddWithKey 查询错误地添加了约束
【发布时间】:2013-07-30 20:59:40
【问题描述】:

此查询在 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 

检查本地变量显示已添加了 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

【问题讨论】:

  • 这个错误信息在什么时候出现?在第二次插入之后?

标签: tsql ado.net sql-server-2012


【解决方案1】:

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

Failed to enable constraints. One or more rows contain values violating non-null, unique, or foreign-key constraints

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

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)。如果你真的想要一个所有的列表,那么我会说一个没有不必要的 JOINing 和 GROUPing 的简化重写将是你最好的第一道攻击线。

【讨论】:

    猜你喜欢
    • 2016-02-01
    • 1970-01-01
    • 2019-03-15
    • 1970-01-01
    • 2016-06-04
    • 1970-01-01
    • 1970-01-01
    • 2014-01-03
    • 2011-01-23
    相关资源
    最近更新 更多