在SQL Server 2005中,新增的OUTPUT子句極大地提高了數據轉移的方便性,不過很遺憾的是, OUTPUT子句的輸出表不能包含啟用狀態的CHECK約束。這未免讓人覺得很不爽。不過,在實際使用過程中,卻發現了一個很奇怪的問題,某些具有啟用狀態的CHECK約束的表確實無法用於OUTPUT子句,而某些表卻是可以的,經過對比,發現了這個很奇怪現象的原因,使得可以把有啟用狀態的CHECK約束的表用於OUTPUT子句。
下面是演示的代碼:
USE tempdb;
GO
-- 建立測試表
CREATE TABLE dbo.tb_source(
id int
);
CREATE TABLE dbo.tb_target(
id int,
CONSTRAINT CHK__tb_target__id
CHECK(
id > 0)
);
GO
-- OUTPUT 測試1
DELETE dbo.tb_source
OUTPUT deleted.*
INTO dbo.tb_target;
/*-- 會收到錯誤
消息333,級別16,狀態1,第3 行
OUTPUT INTO 子句的目標表'dbo.tb_target' 不能具有任何啟用的檢查約束或任何啟用的規則。找到檢查約束或規則'CHK__tb_target__id'。
--*/
GO
-- 禁用和啟用約束
ALTER TABLE dbo.tb_target
NOCHECK CONSTRAINT ALL;
ALTER TABLE dbo.tb_target
CHECK CONSTRAINT ALL;
GO
-- OUTPUT 測試2
DELETE dbo.tb_source
OUTPUT deleted.*
INTO dbo.tb_target;
/*-- 測試成功
(0 行受影響)
--*/
GO
-- 驗證CHECK 約束是否正常工作
INSERT dbo.tb_target
VALUES(
-1);
/*-- 結果(違反約束)
消息547,級別16,狀態0,第3 行
INSERT 語句與CHECK 約束"CHK__tb_target__id"沖突。該沖突發生於數據庫"tempdb",表"dbo.tb_target", column 'id'。
語句已終止。
--*/
GO
-- 驗證OUTPUT 時, CHECK 約束是否正常工作
INSERT dbo.tb_source
VALUES(
-1);
DELETE dbo.tb_source
OUTPUT deleted.*
INTO dbo.tb_target;
/*-- 結果(違反約束)
(1 行受影響)
消息547,級別16,狀態0,第6 行
DELETE 語句與CHECK 約束"CHK__tb_target__id"沖突。該沖突發生於數據庫"tempdb",表"dbo.tb_target", column 'id'。
語句已終止。
--*/
GO
-- 刪除測試
DROP TABLE dbo.tb_source, dbo.tb_target;
從測試可以看出,只要CHECK約束是使用WITH NOCHECK啟用的,則可以作為OUTPUT子句的輸出表(與微軟給的錯誤提示信息顯示不一樣)。
而且這種情況,同樣適用於使用WITH NOCHECK創建的約束,參考下面的腳本。
USE tempdb;
GO
-- 建立測試表
CREATE TABLE dbo.tb_source(
id int
);
CREATE TABLE dbo.tb_target(
id int
);
ALTER TABLE dbo.tb_target
WITH NOCHECK
ADD CONSTRAINT CHK__tb_target__id
CHECK(
id > 0);
GO
-- OUTPUT 測試
DELETE dbo.tb_source
OUTPUT deleted.*
INTO dbo.tb_target;
/*-- 測試成功
(0 行受影響)
--*/
GO
-- 刪除測試
DROP TABLE dbo.tb_source, dbo.tb_target;
注:
1. 默認情況下,建立約束使用的是WITH CHECK,而啟用約束使用的是WITH NOCHECK;
2. WITH NOCHECK只是確定在建立(啟用)約束時,是否檢查表中的現有數據,對新進的數據沒有影響;
3. WITH NOCHECK會導致分區視圖失敗。