如何在 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”的外键的主要内容,如果未能解决你的问题,请参考以下文章

如何在单个结果集中列出 SQL Server 中所有数据库中的所有表?

如何列出 SQL Server 中的所有连接?

当表在不同的架构中时,如何从 SQL Server 导入所有带有 sqoop 的表?

列出 SQL Server 中所有删除的数据库

列出SQL Server数据库中的所有序列

Sql Query 列出 SQL Server 2005 数据库中的所有视图