Sql-Server

執行 ALTER TABLE 後仍然可以看到約束我_噓__米是sCHmysch.我_tablename米是噸一種b一世和n一種米和mytablenameNOCHECK 約束所有,但為什麼?

  • August 20, 2021

我正在查看 proc 中的一段程式碼,該程式碼似乎禁用但從未重新啟用約束,即

ALTER TABLE [mysch].[mytablename] NOCHECK CONSTRAINT ALL 

但是在下面的程式碼中執行了這些操作之後,就沒有什麼可以重新啟用它們了。

我什至嘗試將“NOCHECK”語句作為一行執行,以查看會發生什麼,即忽略proc 中的其餘程式碼。

我假設它們仍然處於啟用狀態,因為如果我轉到對象資源管理器、表格(加上右鍵點擊“刷新”)然後編寫表格腳本,我可以看到幾十行,例如

ALTER TABLE [mysch].[mytablename]  WITH NOCHECK ADD  CONSTRAINT [FK_mytable_othertable] FOREIGN KEY([othertable_ID])
REFERENCES [mysch].[othertable] ([othertable_ID])
GO

這裡發生了什麼?我使用 Red Gate 的 SQL 搜尋搜尋了其他對像以查找 ALTER TABLE..CHECK 語句,但無濟於事。

對象資源管理器是否錯誤地顯示了約束或者這裡有其他東西在起作用?

這裡發生了什麼?

有人“禁用”了表上的所有約束,這在大型數據修改之前有點常見,因為檢查約束的過程比以後重新啟用它們更慢。

僅僅因為約束沒有被檢查並不意味著它們被丟棄了。您可以NOCHECK在複製的約束語法中看到語法。

您可以通過查詢描述它們的視圖來查看哪些約束可能被禁用或不受信任:

SELECT
   c.name,
   c.is_not_trusted,
   c.is_disabled
FROM sys.check_constraints AS c
WHERE c.is_not_trusted = 1 
OR    c.is_disabled = 1;

SELECT
   f.name,
   f.is_not_trusted,
   f.is_disabled
FROM sys.foreign_keys AS f
WHERE f.is_not_trusted = 1 
OR    f.is_disabled = 1;

更多詳情請看我的文章:

重新啟用/信任約束的腳本(從第一個連結複製):

SELECT N'ALTER TABLE ' 
      + QUOTENAME(s.name) 
      + N'.' 
      + QUOTENAME(o.name) 
      + N' WITH CHECK CHECK CONSTRAINT '
      + QUOTENAME(f.name) AS utrusted_fk
FROM sys.foreign_keys AS f
INNER JOIN sys.objects AS o
   ON f.parent_object_id = o.object_id
INNER JOIN sys.schemas AS s
   ON o.schema_id = s.schema_id
WHERE f.is_not_trusted = 1;

SELECT N'ALTER TABLE ' 
      + QUOTENAME(s.name) 
      + N'.' 
      + QUOTENAME(o.name) 
      + N' WITH CHECK CHECK CONSTRAINT '
      + QUOTENAME(c.name) AS utrusted_ck
FROM sys.check_constraints AS c
INNER JOIN sys.objects AS o
   ON c.parent_object_id = o.object_id
INNER JOIN sys.schemas AS s
   ON o.schema_id = s.schema_id
WHERE c.is_not_trusted = 1;

ANSI SQL 的語法非常混亂。您有兩個方面的約束:

  1. 它是啟用還是禁用?
  2. 如果表中存在“無效”數據的可能性?

對於上面的第 2 點,有兩種情況

  1. 您在未檢查現有數據的情況下添加了約束
  2. 您禁用約束,然後在重新啟用它時,您沒有檢查現有數據。

語法令人困惑,因為它使用 CHECK 來表示“檢查現有數據”和“啟用或禁用約束”的概念。這導致如下命令。下面的程式碼沒有錯別字!

--Enable constraint without checking existing data
ALTER TABLE t WITH NOCHECK CHECK CONSTRAINT t_ch

--Enable constraint AND check existing data
ALTER TABLE t WITH CHECK CHECK CONSTRAINT t_ch

正如 Erik 向我們展示的那樣,您可以使用元數據進行檢查。

  1. is_not_trusted 列告訴我們您是否添加或啟用了約束而不檢查現有數據(WITH CHECK 或 WITH NOCHECK)
  2. is_disabled 告訴我們約束是啟用還是禁用(CHECK 或 NOCHECK)

我希望 ANSI 會選擇 DISABLE 和 ENABLE 這兩個詞來禁用和啟用約束,這將使命令更容易理解……

帶註釋的例子:

USE tempdb
GO

DROP TABLE IF EXISTS t
GO

CREATE TABLE t(c1 int)
GO

--Add the constraint without checking existing data
ALTER TABLE t WITH NOCHECK ADD CONSTRAINT t_ch CHECK(c1 > 10)
GO

--Constraint is enabled, below fails
INSERT INTO t VALUES (1)
GO

--Disable constraint
ALTER TABLE t NOCHECK CONSTRAINT t_ch
GO

--Constraint is NOT enabled, below executes successfully
INSERT INTO t VALUES (1)
GO

--Enable constraint without checking existing data
ALTER TABLE t WITH NOCHECK CHECK CONSTRAINT t_ch
GO

--Constraint is enabled, below fails
INSERT INTO t VALUES (1)
GO

--Disable constraint
ALTER TABLE t NOCHECK CONSTRAINT t_ch
GO

--Enable constraint AND check existing data, command fails because of invalid data in table
ALTER TABLE t WITH CHECK CHECK CONSTRAINT t_ch
GO

--Check of data in a table violates a constraint (not ANSI SQL, obviouly)
DBCC CHECKCONSTRAINTS(t_ch)

引用自:https://dba.stackexchange.com/questions/298264