web-dev-qa-db-ja.com

SQL Serverで「WITH NOCHECK」を使用してすべての外部キーを一覧表示する方法

「WITH NOCHECK」の説明が適用されたデータベース内のすべての外部キーをリストするクエリを知っている人はいますか? (それらを削除すると、パフォーマンスと安定性が向上します)。

40
digiguru

以下は、無効になっている現在のデータベースの外部キーの名前を返します(WITH NOCHECKなど)。

SQL Server 2005/2008の場合:

select * from sys.foreign_keys where is_disabled=1




-- 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)。

38
Nick Kavadias
SELECT * FROM sys.foreign_keys AS f Where Is_Not_Trusted = 1
10
digiguru

次のスクリプトは、既存のデータをチェックし、現在信頼されていない( '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
9
Scott Munro

WITH NOCHECKはFKの一時的にのみ適用する必要があります。そうしないと、リンクされた記事が指摘するように、オプティマイザにとって役に立たなくなります。 BOLから:

クエリオプティマイザーは、WITH NOCHECKで定義された制約を考慮しません。このような制約は、ALTER TABLE table CHECK CONSTRAINT ALLを使用して再度有効にするまで無視されます。

これにより、すべての外部キーが識別されます:(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'

参照

余談ですが、SQL Server 2000と2005の両方で、次のコマンドを使用して、データが制約に違反しているかどうかを確認できます。

DBCC CHECKCONSTRAINTS (table_name)
5
Mitch Wheat

次のコードは、「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
3
CodeGrue

これは古い質問であり、いくつかの古い回答といくつかの良い情報があります。ただし、この問題の領域に対処するために使用しているスクリプトをいくつかの異なるデータベースで共有したかっただけです。

-- 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でうまく機能します。

0
Jaxidian