【问题标题】:Cascade Update with Autogenerated Update Stored Procedure from ADO.NET使用来自 ADO.NET 的自动生成更新存储过程的级联更新
【发布时间】:2010-11-02 20:52:35
【问题描述】:

我正在运行带有 .NET 2.0 的 MS SQL Server 2005。我当前的应用程序是用 C# 编写的。

在 MSSQL 中,我创建了 2 个测试表来说明我的问题:

表1设置为:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[T1](
 [n] [bigint] NOT NULL,
 [t] [varchar](10) NOT NULL,
 CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED 
(
 [n] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF

表2设置为:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[T2](
 [n] [bigint] NULL,
 [Test] [varchar](4) NOT NULL,
 [Num] [bigint] NULL
) ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[T2]  WITH CHECK ADD  CONSTRAINT [FK_T2_T1] FOREIGN KEY([n])
REFERENCES [dbo].[T1] ([n])
ON UPDATE CASCADE
GO
ALTER TABLE [dbo].[T2] CHECK CONSTRAINT [FK_T2_T1]

我已经用多行填充了每个。

简而言之,表 1 (T1) 有一个 bigint 主键字段“n”,它映射到子表 T2 的 n 字段。设置了级联更新,因此当 T1.n 被修改时,T2.n 也会被更新。如果我做一个简单的查询并在其中一行中设置 T1.n 的值,我可以看到级联发生在执行计划中。如果我设置了 T1.t 的值,级联不会按预期出现在执行计划中。

在 ADO.NET 中,我在表单中添加了一个 DataAdapter 并让它自动生成 4 个存储过程,T1 的更新显示如下:(我添加了一些间距以帮助提高可读性)

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[T1_Update]
(
 @n bigint,
 @t varchar(10),
 @Original_n bigint,
 @Original_t varchar(10)
)
AS
 SET NOCOUNT OFF;
UPDATE [T1] 
SET 
[n] = @n, 
[t] = @t 
WHERE (
([n] = @Original_n) AND 
([t] = @Original_t));

SELECT n, t FROM T1 WHERE (n = @n)

我可以执行这个存储过程:

DECLARE @RC int
DECLARE @n bigint
DECLARE @t varchar(10)
DECLARE @Original_n bigint
DECLARE @Original_t varchar(10)

EXECUTE @RC = [T1_Update] 
   5, '5', 5, '4' -- n, t, Original_n, Original_t

在这种情况下,n 的值不变,t 的值发生变化。我希望看到与我刚刚执行“UPDATE T1 SET t='4' where n=5 and t='5'”时相同的行为/执行计划。运行此查询,执行计划非常简单,其中包括 CI 搜索、计算标量、CI 更新、更新。

但是,运行上面的执行,执行计划显示它扫描表 T2 并在所述表上执行更新,即使值没有更改。

我认为这是由于以下几行:

UPDATE [T1] 
SET 
[n] = @n, 

从 T1_Update 存储过程中注释掉 [n] = @n,即使值没有改变,也会停止触发级联更新。

所以假设我想保留级联更新,有没有办法重写/修改自动生成的 T1_Update 存储过程,以便级联更新仅在“n”的值更改时激活,而不是在值时激活没变?

编辑:我删除了 T1 上的一个外键关系,该关系引用了我在设置此测试时意外添加的自身。

【问题讨论】:

    标签: sql sql-server ado.net


    【解决方案1】:

    我谨表示,如果您要更新主键字段,则它不是主键。要使列成为主键,以下三个语句必须为真:

    1. 绝对不能为 NULL。
    2. 它必须是唯一的。
    3. 绝对不能改变。

    前两个通常由数据库服务器强制执行,但大多数不强制执行第三个,而在您的设计中似乎违反了第三个。根据我的经验,这是你真的不想做的事情。仅仅因为数据库允许您这样做并不意味着这是一个好主意。这里有龙。

    好的,您已收到警告。至于如何做你想做的事 - 我建议你放弃外键约束上的 ON UPDATE CASCADE 选项,而是在父表上创建一个 ON UPDATE 触发器。如果您在更新后将其设置为触发,则 OLD 和 NEW 值都将可用,并且您的代码可以测试以准确查看哪些字段已更改并采取适当的措施。您可能还需要将外键设置为在 COMMIT 而不是动词时间触发 - 否则违反约束的短时间内将导致更新失败,即使它会在 COMMIT 时被纠正发出。 (我对 SQL Server 不像对其他数据库(例如 Oracle)那样熟悉,而且我不是 100% 肯定你可以将约束执行推迟到 SQL Server 下的 COMMIT 时间。你可能需要做一些挖掘)。

    分享和享受。

    【讨论】:

    • 了解,我们目前正在重新评估之前设计到数据库中的所有 Cascade 更新以删除它们。本来以为级联更新不会有什么坏处,如果密钥真的不变,级联就不会触发。然而,我们发现调用了自动生成的更新过程,即使值没有改变,它也在“改变”这个值。所以我试图弄清楚为什么会发生这种级联,以及如何避免它。仍然对为什么会级联感到困惑。
    • @Equixor:刚刚注意到一件事——为什么 T1.n 上有一个外键指向 T1.n?也就是说,该领域是指自己? ???我以前从未见过这样做过。
    • 当我添加对测试的引用时,我必须在 T1 上添加一个关系并点击关闭。按下 Add 后,如果没有修改它不会自动删除它,并且默认是对自身的引用(这是无用的)。我删除了这个引用,因为它显然不需要并且级联行为没有受到影响。
    猜你喜欢
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 2012-09-10
    • 2019-12-02
    相关资源
    最近更新 更多