一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

數(shù)據(jù)操作技巧: 禁用約束和觸發(fā)器

 順溜的書架 2015-01-15
數(shù)據(jù)操作技巧
禁用約束和觸發(fā)器
John Papa

代碼下載位置: DataPoints2007_04.exe (153 KB)
Browse the Code Online
約束是在數(shù)據(jù)庫中維護(hù)數(shù)據(jù)一致性的重要工具。但是,有時(shí)禁用一個(gè)或多個(gè)約束來執(zhí)行某些任務(wù)(例如,使用脫機(jī)數(shù)據(jù)庫進(jìn)行數(shù)據(jù)同步)會(huì)更方便。在使用 SQL Server? 復(fù)制技術(shù)同步數(shù)據(jù)庫之間的數(shù)據(jù)時(shí),可以告知單個(gè)對象在復(fù)制過程中不強(qiáng)制約束。
例如,在使用 NOT FOR REPLICATION 語句定義外鍵約束時(shí),SQL Server 在復(fù)制過程中將不會(huì)強(qiáng)制約束。實(shí)際上,NOT FOR REPLICATION 語句可以直接用于定義外鍵約束、檢查約束、標(biāo)識(shí)和觸發(fā)器的 T-SQL 語句中。對于使用 SQL Server 復(fù)制的操作,在適當(dāng)?shù)膶ο笊鲜褂?NOT FOR REPLICATION 語句是一種不錯(cuò)的選擇。但是,如果您要手動(dòng)執(zhí)行數(shù)據(jù)同步,則另一個(gè)方法是手動(dòng)禁用約束和觸發(fā)器。
通常在需要同步數(shù)據(jù)子集以及需要更好地控制數(shù)據(jù)同步方法的聯(lián)機(jī)/脫機(jī)應(yīng)用程序中執(zhí)行手動(dòng)同步。在本月的專欄中,我將討論何時(shí)手動(dòng)禁用和啟用約束會(huì)更有利,此方法可以幫助您解決哪些類型的問題以及一些解決問題的技巧。

禁用外鍵
我不建議從關(guān)系數(shù)據(jù)庫中刪除外鍵約束。但是,有時(shí)(例如在一系列表上執(zhí)行大量的插入和更新操作以及需要更準(zhǔn)確的結(jié)果和更佳的性能)您可能需要臨時(shí)減少對一個(gè)或多個(gè)外鍵的引用完整性檢查。當(dāng)然,您只能在以正常的關(guān)系順序無法對整個(gè)數(shù)據(jù)庫執(zhí)行大批量的數(shù)據(jù)更新時(shí)使用此方法。
因此何時(shí)才應(yīng)禁用外鍵約束呢?假設(shè)您的關(guān)系數(shù)據(jù)結(jié)構(gòu)有許多表,所有表都通過外鍵約束以某種方式彼此相關(guān)。與此數(shù)據(jù)庫交互的應(yīng)用程序具有一個(gè)脫機(jī)/移動(dòng)版本,該版本與可能駐留在便攜式計(jì)算機(jī)上的數(shù)據(jù)庫的第二個(gè)實(shí)例進(jìn)行通信。對主數(shù)據(jù)庫所做的數(shù)據(jù)更改可能需要與脫機(jī)/移動(dòng)數(shù)據(jù)庫同步,而同步數(shù)據(jù)的方法有好幾種。
同步數(shù)據(jù)的一個(gè)方法是將插入、更新和刪除操作應(yīng)用于脫機(jī)/移動(dòng)數(shù)據(jù)庫,以便與關(guān)系結(jié)構(gòu)相一致。例如,在客戶相應(yīng)的訂單前面插入客戶,并在相應(yīng)的訂單詳細(xì)信息前面插入訂單。記錄刪除將按相反的方向進(jìn)行(從子記錄到父記錄)。但是,在大型數(shù)據(jù)庫結(jié)構(gòu)上應(yīng)用此方法時(shí),可能因?yàn)樘珡?fù)雜而無法實(shí)現(xiàn)和維護(hù)。
另一個(gè)方法是刪除外鍵約束,之后同步數(shù)據(jù),然后重新創(chuàng)建外鍵約束。此方法只需進(jìn)行比較小的改動(dòng)(只需禁用外鍵約束)即可正常運(yùn)行。禁用外鍵約束之后,可以同步數(shù)據(jù),然后可以再次啟用外鍵。禁用外鍵的語法如下所示:
-- Disable foreign key constraint
ALTER TABLE Orders
    NOCHECK CONSTRAINT 
        FK_Orders_Customers

-- Add a new Order record for a non-existent customer
INSERT INTO Orders (CustomerID) VALUES ('BLAH')

-- Select all orders for the non-existent customer
SELECT * FROM Orders WHERE CustomerID = 'BLAH'
此外鍵強(qiáng)制“訂單”表中的 CustomerID 必須是“客戶”表中的有效 CustomerID。代碼會(huì)禁用外鍵然后將訂單插入到“訂單”表。插入的訂單記錄的 CustomerID 在父“客戶”表中不存在。由于外鍵已禁用,完整性檢查會(huì)被忽略,訂單記錄將成功插入。
以下代碼顯示了重新啟用外鍵約束然后測試外鍵約束工作是否正常的方法。執(zhí)行此代碼時(shí),由于強(qiáng)制約束,訂單并未被插入。系統(tǒng)將返回錯(cuò)誤消息,表明插入語句與外鍵約束出現(xiàn)沖突。
-- Enable foreign key constraint
ALTER TABLE Orders
    CHECK CONSTRAINT 
        FK_Orders_Customers

-- Add a new Order record for a non-existent customer
INSERT INTO Orders (CustomerID) VALUES ('BLEH')
在禁用外鍵、觸發(fā)器和其他約束時(shí),一定要確保在該時(shí)間段內(nèi)不會(huì)在數(shù)據(jù)庫上執(zhí)行任何數(shù)據(jù)操作語言 (DML) 操作。這必須在手動(dòng)執(zhí)行禁用選項(xiàng)時(shí)進(jìn)行處理。如果您使用 SQL Server 復(fù)制和 NOT FOR REPLICATION 語句同步數(shù)據(jù),將會(huì)自動(dòng)處理該條件。

禁用觸發(fā)器
也可以在需要的時(shí)候禁用觸發(fā)器。有時(shí)您并不想在同步數(shù)據(jù)時(shí)激發(fā)觸發(fā)器。例如,如果您更新脫機(jī)/移動(dòng)數(shù)據(jù)庫(如上例所示)時(shí),您可能沒有注意到幾個(gè)表中可能有觸發(fā)器。在將行插入觸發(fā)器所在的表從而導(dǎo)致該行被再次插入另一個(gè)表時(shí),可能會(huì)激發(fā)這些觸發(fā)器。但是在大型數(shù)據(jù)同步中,您可能并不希望再次插入這些類型的插入。為了避免出現(xiàn)這種不確定的事情,您可以臨時(shí)禁用觸發(fā)器,如下所示:
DISABLE TRIGGER MyTriggerName ON MyTableName
與之相反,若要啟用觸發(fā)器,請執(zhí)行以下代碼:
ENABLE TRIGGER MyTriggerName ON MyTableName
以下代碼顯示了當(dāng)在“區(qū)域”表中插入或更新一行或多行時(shí)會(huì)激發(fā)的觸發(fā)器(并顯示一條信息)。
CREATE TRIGGER trRegion_InsertUpdate ON Region
    FOR INSERT, UPDATE
AS
    PRINT ' Trigger is running. ' 
          +  CAST(@@ROWCOUNT AS VARCHAR(10)) 
          + ' row(s) affected.'
GO
您可以通過將一個(gè)新區(qū)域插入到“區(qū)域”表中測試此觸發(fā)器,如下所示:
INSERT INTO Region (RegionID, RegionDescription) 
VALUES (5, 'Some New Region')
在查詢窗口中執(zhí)行此代碼時(shí),新區(qū)域?qū)⒈徊迦搿皡^(qū)域”表中,然后觸發(fā)器會(huì)被激發(fā),消息窗格中將顯示一條消息。若要禁用觸發(fā)器,您可以執(zhí)行以下代碼:
DISABLE TRIGGER trRegion_InsertUpdate ON Region
那么,如果您執(zhí)行此代碼來更新新區(qū)域,則數(shù)據(jù)會(huì)被更新而不會(huì)激發(fā)觸發(fā)器:
UPDATE Region SET RegionDescription = 'A Great Region' 
WHERE RegionID = 5
若要重新啟用觸發(fā)器,只需執(zhí)行以下查詢:
ENABLE TRIGGER trRegion_InsertUpdate ON Region

同步脫機(jī)/移動(dòng)數(shù)據(jù)庫
禁用單個(gè)觸發(fā)器或外鍵會(huì)很有用,但是在我介紹的脫機(jī)/移動(dòng)數(shù)據(jù)庫同步方案中,一次禁用所有觸發(fā)器或外鍵將非常有用。若要執(zhí)行此操作,您應(yīng)執(zhí)行以下步驟:禁用所有外鍵約束,禁用所有觸發(fā)器,執(zhí)行插入、更新和刪除操作,啟用所有外鍵約束,并最終重新啟用所有觸發(fā)器。
通過禁用觸發(fā)器和外鍵約束,已經(jīng)修改了數(shù)據(jù)的訂單會(huì)變得不一致。在小型數(shù)據(jù)庫(例如,羅斯文數(shù)據(jù)庫)中,這種節(jié)省可能微不足道;但是在具有數(shù)十個(gè)或數(shù)百個(gè)表和關(guān)系的大型數(shù)據(jù)庫中,將會(huì)節(jié)省大量時(shí)間。此外,如果沒有此方法,則每次數(shù)據(jù)庫架構(gòu)添加新表和新關(guān)系時(shí),您將不得不在腳本中修改表的順序以便插入/更新/刪除數(shù)據(jù)。禁用外鍵約束和觸發(fā)器之后,由于順序不再重要,因此您只需將表添加到腳本末尾即可。
請注意最后啟用觸發(fā)器和外鍵很重要,而不管腳本是否成功。例如,如果您的腳本禁用約束和觸發(fā)器,并嘗試修改數(shù)據(jù),但未成功;您仍希望最后啟用約束和觸發(fā)器。

光標(biāo)和信息架構(gòu)視圖
若要禁用所有外鍵,必須首先收集外鍵以及每個(gè)外鍵適用的表的列表。這里,您可以借助于光標(biāo)和 INFORMATION_SCHEMA 視圖。名為 INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS 的視圖將返回當(dāng)前數(shù)據(jù)庫中的外鍵列表。若要獲得外鍵適用的表的名稱,請使用名為 INFORMATION_SCHEMA.TABLE_CONSTRAINTS 的視圖。通過將以下代碼加入這兩個(gè)視圖可以返回所有外鍵及其相應(yīng)表的列表。
SELECT
  ref.constraint_name AS FK_Name,
  fk.table_name AS FK_Table
FROM
  INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref
  INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk 
  ON ref.constraint_name = fk.constraint_name
ORDER BY
  fk.table_name,
  ref.constraint_name 

禁用所有外鍵
有了這個(gè)列表后,您可以編寫腳本來禁用外鍵。您可以聲明并打開光標(biāo),循環(huán)訪問該列表,然后創(chuàng)建用于禁用外鍵的 T-SQL 命令,并針對每個(gè)外鍵執(zhí)行該命令(請參見圖 1)。
 Figure 1 禁用和啟用所有外鍵
CREATE PROCEDURE pr_Disable_Triggers_v2 
    @disable BIT = 1
AS 
    DECLARE
        @sql VARCHAR(500),
        @tableName VARCHAR(128),
        @tableSchema VARCHAR(128)

    -- List of all tables
    DECLARE triggerCursor CURSOR
        FOR
    SELECT
        t.TABLE_NAME AS TableName,
        t.TABLE_SCHEMA AS TableSchema
    FROM
        INFORMATION_SCHEMA.TABLES t
    ORDER BY
        t.TABLE_NAME,
        t.TABLE_SCHEMA 

    OPEN triggerCursor

    FETCH NEXT FROM triggerCursor 
    INTO @tableName, @tableSchema

    WHILE ( @@FETCH_STATUS = 0 )
        BEGIN
            IF @disable = 1 
                SET @sql = ‘ALTER TABLE ‘ + @tableSchema 
                    + ‘.[‘ + @tableName + ‘] DISABLE TRIGGER ALL’ 
            ELSE 
                SET @sql = ‘ALTER TABLE ‘ + @tableSchema 
                    + ‘.[‘ + @tableName + ‘] ENABLE TRIGGER ALL’ 

            PRINT ‘Executing Statement - ‘ + @sql

            EXECUTE ( @sql )
            FETCH NEXT FROM triggerCursor
            INTO @tableName, @tableSchema
        END

    CLOSE triggerCursor
    DEALLOCATE triggerCursor
首先,foreignKeyCursor 光標(biāo)被聲明為收集外鍵及其表名稱列表的 SELECT 語句。接下來,打開光標(biāo)并執(zhí)行初始 FETCH 語句。此 FETCH 語句將第一行的數(shù)據(jù)讀取到局部變量 @foreignKeyName 和 @tableName。
在循環(huán)光標(biāo)時(shí),您可以看到 @@FETCH_STATUS 的值為 0,這表示提取成功。這意味著循環(huán)將會(huì)繼續(xù),因此可以從行集中獲取各個(gè)后續(xù)外鍵。
連接上的所有光標(biāo)都可以使用 @@FETCH_STATUS。因此,如果您正在循環(huán)多個(gè)光標(biāo),一定要檢查緊跟著 FETCH 語句的語句中 @@FETCH_STATUS 的值。@@FETCH_STATUS 將反映連接中最近的 FETCH 操作的狀態(tài)。@@FETCH_STATUS 的有效值包括:
  • 0 = FETCH 成功
  • -1 = FETCH 失敗
  • -2 = 提取的行丟失
在循環(huán)內(nèi),代碼會(huì)根據(jù)不同意圖(禁用還是啟用外鍵約束)來構(gòu)建不同的 ALTER TABLE 命令(使用 CHECK 或 NOCHECK 關(guān)鍵字)。接下來,語句將被打印成一條消息(這樣可以看到其進(jìn)度),然后執(zhí)行該語句。最后在循環(huán)處理所有行之后,存儲(chǔ)過程關(guān)閉并釋放光標(biāo)。

禁用所有觸發(fā)器
圖 1 中的存儲(chǔ)過程將禁用或啟用數(shù)據(jù)庫中的所有外鍵。在某些情況下,您可能希望在數(shù)據(jù)同步過程中禁用所有觸發(fā)器。圖 2 中的 pr_Disable_Triggers 存儲(chǔ)過程正好可以完成該操作。
 Figure 2 禁用和啟用所有觸發(fā)器
CREATE PROCEDURE pr_Disable_Triggers 
@disable BIT = 1
AS 
    DECLARE
        @sql VARCHAR(500),
        @tableName VARCHAR(128),
        @triggerName VARCHAR(128),
        @tableSchema VARCHAR(128)

    -- List of all triggers and tables that exist on them
    DECLARE triggerCursor CURSOR
        FOR
    SELECT
        so_tr.name AS TriggerName,
        so_tbl.name AS TableName,
        t.TABLE_SCHEMA AS TableSchema
    FROM
        sysobjects so_tr
    INNER JOIN sysobjects so_tbl ON so_tr.parent_obj = so_tbl.id
    INNER JOIN INFORMATION_SCHEMA.TABLES t 
    ON 
        t.TABLE_NAME = so_tbl.name
    WHERE
        so_tr.type = ‘TR’
    ORDER BY
        so_tbl.name ASC,
        so_tr.name ASC

    OPEN triggerCursor

    FETCH NEXT FROM triggerCursor 
    INTO @triggerName, @tableName, @tableSchema

    WHILE ( @@FETCH_STATUS = 0 )
        BEGIN
            IF @disable = 1 
                SET @sql = ‘DISABLE TRIGGER [‘ 
                    + @triggerName + ‘] ON ‘ 
                    + @tableSchema + ‘.[‘ + @tableName + ‘]’
            ELSE 
                SET @sql = ‘ENABLE TRIGGER [‘ 
                    + @triggerName + ‘] ON ‘ 
                    + @tableSchema + ‘.[‘ + @tableName + ‘]’

            PRINT ‘Executing Statement - ‘ + @sql
            EXECUTE ( @sql )
            FETCH NEXT FROM triggerCursor 
            INTO @triggerName, @tableName,  @tableSchema
        END

    CLOSE triggerCursor
    DEALLOCATE triggerCursor
pr_Disable_Triggers 存儲(chǔ)過程將獲取一個(gè)包含當(dāng)前數(shù)據(jù)庫中所有觸發(fā)器及其相應(yīng)表名稱(和架構(gòu))的行集。由于沒有 INFORMATION_SCHEMA.TRIGGERS 視圖,我將從 SQL Server 2005 系統(tǒng)表和 INFORMATION_SCHEMA 視圖中收集觸發(fā)器列表和相關(guān)信息(如果可能)。
sysobjects 系統(tǒng)表位于所有數(shù)據(jù)庫中,可以通過查詢來返回所有觸發(fā)器或表的列表。此系統(tǒng)表本身也可以加入以獲取要操作的觸發(fā)器和表列表,如圖 2 中的光標(biāo)聲明所示。
如果在數(shù)據(jù)庫上使用諸如 AdventureWorks 的存儲(chǔ)過程(表在特定的架構(gòu)中),您必須將架構(gòu)名稱作為表名稱的前綴。pr_Disable_Triggers 存儲(chǔ)過程通過加入 INFORMATION_SCHEMA.TABLES 視圖來處理此情況,該視圖返回表的 SCHEMA_NAME。
編寫要禁用和重新啟用觸發(fā)器和外鍵的例程后,您可以在腳本中使用這些例程(該腳本用于修改脫機(jī)/移動(dòng)數(shù)據(jù)庫以使其與主數(shù)據(jù)庫保持同步)。例如,您可以使用以下腳本(將中間的注釋替換為數(shù)據(jù)操作運(yùn)算)。
pr_Disable_Foreign_Keys  1
pr_Disable_Triggers 1
-- Perform data operations
pr_Disable_Foreign_Keys  0
pr_Disable_Triggers 0

迅速禁用所有觸發(fā)器
您可以修改圖 2 中顯示的 pr_Disable_Triggers 存儲(chǔ)過程來執(zhí)行與前面稍有不同的 T-SQL 命令(該命令將啟用或禁用所有觸發(fā)器)。其中的 T-SQL 語句將使用 ALTER TABLE 命令(禁用或啟用一個(gè)表中的所有觸發(fā)器)的風(fēng)格。通過使用該方法,您可以修改 pr_Disable_Triggers 存儲(chǔ)過程,針對每個(gè)表執(zhí)行以下語句來禁用該表上的所有觸發(fā)器:
ALTER TABLE MySchemaName.MyTableName DISABLE TRIGGER ALL
通過使用該語法,光標(biāo)的查詢將收集全部表名稱,而無需收集觸發(fā)器本身的名稱。因此,可以通過修改存儲(chǔ)過程來使用該方法遍歷表列表并啟用或禁用每個(gè)表上的所有觸發(fā)器。修改后的存儲(chǔ)過程如圖 3 所示。
 Figure 3 使用 ALTER TABLE 命令禁用所有觸發(fā)器
CREATE PROCEDURE pr_Disable_Foreign_Keys
    @disable BIT = 1
AS
    DECLARE
        @sql VARCHAR(500),
        @tableName VARCHAR(128),
        @foreignKeyName VARCHAR(128)

    -- A list of all foreign keys and table names
    DECLARE foreignKeyCursor CURSOR
    FOR SELECT
        ref.constraint_name AS FK_Name,
        fk.table_name AS FK_Table
    FROM
        INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS ref
        INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk 
    ON ref.constraint_name = fk.constraint_name
    ORDER BY
        fk.table_name,
        ref.constraint_name 

    OPEN foreignKeyCursor

    FETCH NEXT FROM foreignKeyCursor 
    INTO @foreignKeyName, @tableName

    WHILE ( @@FETCH_STATUS = 0 )
        BEGIN
            IF @disable = 1
                SET @sql = ‘ALTER TABLE [‘ 
                    + @tableName + ‘] NOCHECK CONSTRAINT [‘ 
                    + @foreignKeyName + ‘]’
            ELSE
                SET @sql = ‘ALTER TABLE [‘ 
                    + @tableName + ‘] CHECK CONSTRAINT [‘ 
                    + @foreignKeyName + ‘]’

        PRINT ‘Executing Statement - ‘ + @sql

        EXECUTE(@sql)
        FETCH NEXT FROM foreignKeyCursor 
        INTO @foreignKeyName, @tableName
    END

    CLOSE foreignKeyCursor
    DEALLOCATE foreignKeyCursor

結(jié)束語
與從 .NET 應(yīng)用程序逐一執(zhí)行各個(gè)數(shù)據(jù)庫操作相比,從 T-SQL 腳本執(zhí)行多個(gè)數(shù)據(jù)庫操作(如本文所述)會(huì)更高效。例如,您可以使用 ADO.NET 收集 .NET 應(yīng)用程序的外鍵列表,然后執(zhí)行相應(yīng)的命令來禁用各個(gè)外鍵。同樣可以將其擴(kuò)展為獲取觸發(fā)器列表,然后禁用觸發(fā)器。所有這些操作都需要在 .NET 代碼和數(shù)據(jù)庫之間來回進(jìn)行操作,這與完全在 T-SQL 的數(shù)據(jù)庫服務(wù)器上執(zhí)行代碼相比會(huì)占用更多的資源。

將您想向 John 詢問的問題和提出的意見發(fā)送至:mmdata@microsoft.com mmdata@microsoft.com.


John Papa 是 ASPSOFT () 的高級 .NET 顧問,也是一名棒球迷,在夏季的夜晚,他的大多數(shù)時(shí)光都是和家人及其忠實(shí)的狗 Kadi 一起為洋基隊(duì)加油助威度過的。John 是 C# 領(lǐng)域的一位 MVP,他撰寫了多本有關(guān) ADO、XML 和 SQL Server 的書籍,而且經(jīng)常在行業(yè)會(huì)議(如 VSLive)上發(fā)表演講,或者在 /blogs/john.papa 上發(fā)表博客文章。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    亚洲少妇一区二区三区懂色| 综合久综合久综合久久| 国产农村妇女成人精品| 欧美欧美日韩综合一区| 国产精品国产亚洲区久久| 国产水滴盗摄一区二区| 老熟妇乱视频一区二区| 国产又大又猛又粗又长又爽| 色一欲一性一乱—区二区三区| 果冻传媒精选麻豆白晶晶| 五月婷婷六月丁香亚洲| 91一区国产中文字幕| 又色又爽又黄的三级视频| 欧美性高清一区二区三区视频 | 国产精品伦一区二区三区在线 | 中文字幕在线五月婷婷| 亚洲国产成人av毛片国产| 伊人国产精选免费观看在线视频 | 欧美成人精品国产成人综合| 亚洲伦理中文字幕在线观看| 日本精品最新字幕视频播放| 中文字幕在线五月婷婷| 亚洲精品深夜福利视频| 精品al亚洲麻豆一区| 国产超薄黑色肉色丝袜| 国产小青蛙全集免费看| 欧美日韩成人在线一区| 国产精品九九九一区二区| 日韩精品一区二区亚洲| 欧洲亚洲精品自拍偷拍| 日韩精品区欧美在线一区| 99久久精品午夜一区| 欧美三级大黄片免费看| 亚洲综合一区二区三区在线| 欧美精品日韩精品一区| 婷婷色国产精品视频一区| 国产成人精品午夜福利| 91蜜臀精品一区二区三区| 亚洲精品福利入口在线| 成人区人妻精品一区二区三区 | 欧美一区二区三区在线播放|