如何在 SQL Server 中列出所有带有“WITH NOCHECK”的外键

Posted

技术标签:

【中文标题】如何在 SQL Server 中列出所有带有“WITH NOCHECK”的外键【英文标题】:How to list out all Foreign Keys with "WITH NOCHECK" in SQL Server 【发布时间】:2010-11-18 05:24:45 【问题描述】:

有没有人知道一个查询来列出数据库中的所有外键并应用WITH NOCHECK 描述? (删除它们将提高性能和稳定性)。

【问题讨论】:

伙计们,我也需要同样的东西,但兼容 sql 2000! 你应该把这个问题的答案改成YOURS (is_not_trusted=1) 我已从问题中删除了一个死链接。也许这个sqlblog.com/blogs/tibor_karaszi/archive/2008/01/12/… 提供了相同的信息? 【参考方案1】:

以下将返回当前数据库中被禁用的外键的名称,即 WITH NOCHECK

对于 SQL Server 2005/2008:

select * from sys.foreign_keys where is_disabled=1


答案中有一些关于禁用和不信任之间的区别的讨论。 以下内容解释了差异 这是一些代码来阐明 is_disabled 和 isnottrusted 之间的区别。
-- drop table t1
-- drop table t2
create table t1(i int not null, fk int not null)
create table t2(i int not null)
-- create primary key on t2
alter table t2
add constraint pk_1 primary key (i)
-- create foriegn key on t1
alter table t1
add constraint fk_1 foreign key (fk)
    references t2 (i)
--insert some records
insert t2 values(100)
insert t2 values(200)
insert t2 values(300)
insert t2 values(400)
insert t2 values(500)
insert t1 values(1,100)
insert t1 values(2,100)
insert t1 values(3,500)
insert t1 values(4,500)
----------------------------
-- 1. enabled and trusted
select name,is_disabled,is_not_trusted from sys.foreign_keys
GO

-- 2. disable the constraint
alter table t1 NOCHECK CONSTRAINT fk_1
select name,is_disabled,is_not_trusted from sys.foreign_keys
GO

-- 3. re-enable constraint, data isnt checked, so not trusted.
-- this means the optimizer will still have to check the column
alter table  t1 CHECK CONSTRAINT fk_1 
select name,is_disabled,is_not_trusted from sys.foreign_keys
GO

--4. drop the foreign key constraint & re-add 
-- it making sure its checked
-- constraint is then enabled and trusted
alter table t1  DROP CONSTRAINT fk_1
alter table t1 WITH CHECK 
add constraint fk_1 foreign key (fk)
    references t2 (i)
select name,is_disabled,is_not_trusted from sys.foreign_keys
GO


--5. drop the foreign key constraint & add but dont check
-- constraint is then enabled, but not trusted
alter table t1  DROP CONSTRAINT fk_1
alter table t1 WITH NOCHECK 
add constraint fk_1 foreign key (fk)
    references t2 (i)
select name,is_disabled,is_not_trusted from sys.foreign_keys
GO

is_disabled 表示约束被禁用

isnottrusted 表示 SQL Server 不相信该列已针对外键表进行了检查。

因此,不能假设重新启用外键约束会得到优化。为确保优化器信任该列,最好删除外键约束并使用 WITH CHECK 选项重新创建它 (4.)

【讨论】:

您可以使用以下代码重新启用约束并同时对其进行检查:ALTER TABLE t1 WITH CHECK CHECK CONSTRAINT fk_1 这比删除并重新创建约束要简单一些。【参考方案2】:
SELECT * FROM sys.foreign_keys AS f Where Is_Not_Trusted = 1

【讨论】:

is_not_trusted 列表示 sql server 没有检查这些值以确保 FK 完整性。即,如果约束曾经应用过 NOCHECK!这非常聪明,实际上优化器将使用它来确定它是否可以信任列上的完整性。所以你需要做的不仅仅是重新启用约束,而是重新检查列 但它没有被“禁用”。您如何建议执行“重新启用”它? +1: digiguru, msdn.microsoft.com/en-us/library/ms189807.aspx 您可以像这样重新检查密钥:ALTER TABLE [schema].[table] CHECK CONSTRAINT [FK_myConstraint]【参考方案3】:

以下脚本将生成 alter 语句,这些语句将检查现有数据并防止当前不受信任的外键('with nocheck')的任何新违规行为。

在 SQL Server Management Studio 中执行以生成脚本,然后将它们复制到查询窗口中执行。

select
    'alter table ' + quotename(s.name) + '.' + quotename(t.name) + ' with check check constraint ' + fk.name +';'
from 
    sys.foreign_keys fk
inner join
    sys.tables t
on
    fk.parent_object_id = t.object_id
inner join
    sys.schemas s
on
    t.schema_id = s.schema_id
where 
    fk.is_not_trusted = 1

【讨论】:

【参考方案4】:

WITH NOCHECK 只能暂时应用于 FK,否则正如您链接的文章所指出的那样,它们对优化器毫无用处。来自 BOL:

查询优化器不考虑 WITH 定义的约束 诺查克。此类约束被忽略 直到它们通过使用重新启用 ALTER TABLE 表检查约束 全部。

这将识别您所有的外键:(处理 WITH NOCHECK 位...)

SELECT C.TABLE_CATALOG [PKTABLE_QUALIFIER], 
       C.TABLE_SCHEMA [PKTABLE_OWNER], 
       C.TABLE_NAME [PKTABLE_NAME], 
       KCU.COLUMN_NAME [PKCOLUMN_NAME], 
       C2.TABLE_CATALOG [FKTABLE_QUALIFIER], 
       C2.TABLE_SCHEMA [FKTABLE_OWNER], 
       C2.TABLE_NAME [FKTABLE_NAME], 
       KCU2.COLUMN_NAME [FKCOLUMN_NAME], 
       RC.UPDATE_RULE, 
       RC.DELETE_RULE, 
       C.CONSTRAINT_NAME [FK_NAME], 
       C2.CONSTRAINT_NAME [PK_NAME], 
       CAST(7 AS SMALLINT) [DEFERRABILITY] 
FROM   INFORMATION_SCHEMA.TABLE_CONSTRAINTS C 
       INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU 
         ON C.CONSTRAINT_SCHEMA = KCU.CONSTRAINT_SCHEMA 
            AND C.CONSTRAINT_NAME = KCU.CONSTRAINT_NAME 
       INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC 
         ON C.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
            AND C.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 
       INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C2 
         ON RC.UNIQUE_CONSTRAINT_SCHEMA = C2.CONSTRAINT_SCHEMA 
            AND RC.UNIQUE_CONSTRAINT_NAME = C2.CONSTRAINT_NAME 
       INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2 
         ON C2.CONSTRAINT_SCHEMA = KCU2.CONSTRAINT_SCHEMA 
            AND C2.CONSTRAINT_NAME = KCU2.CONSTRAINT_NAME 
            AND KCU.ORDINAL_POSITION = KCU2.ORDINAL_POSITION 
WHERE  C.CONSTRAINT_TYPE = 'FOREIGN KEY'

Ref.

顺便说一句,在 SQL Server 2000 和 2005 中,您可以使用以下方法检查是否有任何数据违反约束:

DBCC CHECKCONSTRAINTS (table_name)

【讨论】:

DBCC CHECKCONSTRAINTS 很有用,但这确实不能回答问题。【参考方案5】:

以下代码检索所有标记为“WITH NOCHECK”的外键,然后使用 ALTER 语句修复它们:

-- configure cursor on all FKs with "WITH NOCHECK"
DECLARE UntrustedForeignKeysCursor CURSOR STATIC FOR
    SELECT  f.name,
            t.name 
    FROM    sys.foreign_keys AS f
            LEFT JOIN sys.tables AS t 
                ON f.parent_object_id = t.object_id 
    Where   Is_Not_Trusted = 1
OPEN UntrustedForeignKeysCursor

-- loop through the untrusted FKs
DECLARE @FKName varchar(100)
DECLARE @TableName varchar(100)
FETCH NEXT FROM UntrustedForeignKeysCursor INTO @FKName, @TableName
WHILE @@FETCH_STATUS = 0
BEGIN

    -- Rebuild the FK constraint WITH CHECK
    EXEC ('ALTER TABLE ' + @TableName + ' WITH CHECK CHECK CONSTRAINT ' + @FKName)

    -- get next user
    FETCH NEXT FROM UntrustedForeignKeysCursor INTO @FKName, @TableName

END

-- cleanup
CLOSE UntrustedForeignKeysCursor

【讨论】:

【参考方案6】:

我知道这是一个老问题,有一些旧答案有一些很好的信息。但是,我只是想分享一个脚本,我一直在使用它来为我们解决很多不同的数据库中的这个问题:

-- Foreign Keys
SELECT 'ALTER TABLE ' + o.name + ' WITH CHECK CHECK CONSTRAINT ' + i.name + ';' AS AlterStatement
from sys.foreign_keys i
INNER JOIN sys.objects o ON i.parent_object_id = o.object_id
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE i.is_not_trusted = 1 AND i.is_not_for_replication = 0;
GO

-- Constraints
SELECT 'ALTER TABLE ' + o.name + ' WITH CHECK CHECK CONSTRAINT ' + i.name + ';' AS AlterStatement
from sys.check_constraints i
INNER JOIN sys.objects o ON i.parent_object_id = o.object_id
INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
WHERE i.is_not_trusted = 1 AND i.is_not_for_replication = 0 AND i.is_disabled = 0;
GO

这将生成一组 ALTER 语句,以解决外键和约束的“NOCHECK”问题。这是基于Brent Ozar 提供的一些查询,但我出于我的目的和易用性进行了调整。这可以通过 UNION 轻松调整,使其成为单个查询。

仅供参考,我只在 Azure SQL 数据库环境中使用过它。我不确定旧版本的 SQL Server 是否存在限制,但它在 Azure 中运行良好。

【讨论】:

以上是关于如何在 SQL Server 中列出所有带有“WITH NOCHECK”的外键的主要内容,如果未能解决你的问题,请参考以下文章