从ADO.NET使用自动生成的更新存储过程进行级联更新

时间:2010-11-02 20:52:35

标签: sql sql-server ado.net

我正在使用.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',其中n = 5且t ='5'”。运行此查询的执行计划非常简单,它可以用于CI搜索,计算标量,CI更新,更新。

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

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

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

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

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

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

1 个答案:

答案 0 :(得分:0)

我谨慎地提出,如果您要更新主键字段,那么它不是主键。要使列成为主键,必须满足以下三个语句:

  1. 绝不能为NULL。
  2. 一定是独一无二的。
  3. 绝对不能改变。
  4. 前两个通常由数据库服务器强制执行,但大多数都不强制执行第三个,并且它是第三个在您的设计中似乎违反的。根据我的经验,这是你真的不想做的事情。仅仅因为数据库允许你这样做并不意味着它是个好主意。这里有龙。

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

    分享并享受。