2010-11-02 59 views
0

我使用.NET 2.0運行MS SQL Server 2005。我目前的應用程序是用C#編寫的。使用自動生成的更新從ADO.NET級聯更新存儲過程

在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)具有映射到子表T2的n字段的bigint主鍵字段「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,即使值不改變,停止級聯更新從擊發。

因此,假設我想保持級聯更新,是否有重寫/修改自動生成的T1_Update存儲過程的方法,以便級聯更新僅在「n」的值發生更改時才激活,價值不變?

編輯:我刪除了T1上的一個外鍵關係,它引用了我在設置此測試時意外添加的內容。

回答

0

我恭敬地提出,如果您要更新主鍵字段,那不是主鍵。對於要作爲主鍵的列,以下三條語句必須爲真:

  1. 它絕不能爲NULL。
  2. 它必須是唯一的。
  3. 它絕不能改變。

前兩個通常由數據庫服務器執行,但大多數不強制執行第三個,而且這是第三個在您的設計中似乎被違反的。根據我的經驗,這是你真的不想做的事情。僅僅因爲數據庫將允許你這樣做並不意味着這是一個好主意。這裏有龍。

好的,你已被警告。至於如何做你想做的事情 - 我建議你在外鍵約束上取消ON UPDATE CASCADE選項,而是在父表上創建一個ON UPDATE觸發器。如果您在更新後將其設置爲觸發,OLD和NEW值將可用,並且您的代碼可以測試以準確查看哪些字段已更改並採取適當的操作。您可能還需要將外鍵設置爲在COMMIT(而不是動詞時間)上觸發 - 否則違反約束的短時間段將導致更新失敗,即使它將在COMMIT發出。 (我不像SQL Server那樣熟悉SQL Server和其他數據庫(例如Oracle),並且我不是100%肯定的,你可以推遲約束執行,直到SQL Server下的COMMIT時間爲止。)你可能需要做一點挖掘。

分享和享受。

+0

瞭解並且我們目前正在重新評估之前設計到數據庫中的所有Cascade更新以刪除它們。起初它被認爲是級聯更新不會傷害事情,如果關鍵是真的不變,級聯不會觸發。但是我們發現,自動生成的更新過程被調用,即使該值沒有變化,它也會「更改」該值。所以我想弄清楚爲什麼這個級聯發生在第一位,以及如何避免它。仍然困惑於爲什麼它是級聯的。 – Equixor 2010-11-03 00:24:06

+0

@Equixor:剛剛注意到了一些事情 - 爲什麼T1.n上有一個外鍵,它指向T1.n?那就是,這個領域是指自己? ???我從來沒有見過這樣做過。 – 2010-11-03 11:06:00

+0

當我添加對測試的引用時,我必須在T1上添加關係並點擊關閉。按下添加後,如果沒有修改,它不會自動刪除它,並且默認是對自身的引用(這是無用的)。我刪除了這個引用,因爲它顯然不是必需的,級聯行爲不受影響。 – Equixor 2010-11-03 18:17:53