CHECK ADD CONSTRAINT 후 CHECK CONSTRAINT vs. ADD CONSTRAINT


133

SQL Server 2008 용 AdventureWorks 예제 데이터베이스를보고 있는데 작성 스크립트에서 다음을 사용하는 경향이 있음을 알 수 있습니다.

ALTER TABLE [Production].[ProductCostHistory] WITH CHECK ADD 
CONSTRAINT [FK_ProductCostHistory_Product_ProductID] FOREIGN KEY([ProductID])
  REFERENCES [Production].[Product] ([ProductID])
GO

바로 뒤에 :

ALTER TABLE [Production].[ProductCostHistory] CHECK CONSTRAINT     
[FK_ProductCostHistory_Product_ProductID]
GO

외래 키 (여기서와 같이), 고유 제약 조건 및 일반 CHECK제약 조건에서 이것을 봅니다 . DEFAULT제약 조건은 다음과 같이 더 익숙한 정규 형식을 사용합니다.

ALTER TABLE [Production].[ProductCostHistory] ADD  CONSTRAINT  
[DF_ProductCostHistory_ModifiedDate]  DEFAULT (getdate()) FOR [ModifiedDate]
GO

첫 번째 방법과 두 번째 방법의 차이점은 무엇입니까?

답변:


94

첫 번째 구문은 중복입니다. WITH CHECK는 새 제약 조건의 기본값이며 제약 조건도 기본적으로 설정되어 있습니다.

이 구문은 SQL 스크립트를 생성 할 때 SQL 관리 스튜디오에서 생성합니다. 테이블의 기본 제약 조건 동작이 변경 되어도 제약 조건이 활성화되도록 일종의 여분의 중복성이 있다고 가정합니다.


12
WITH CHECK가 실제로 기본값 인 것처럼 보이지 않으며 새 데이터의 기본값 일뿐입니다. 에서 msdn.microsoft.com/en-us/library/ms190273.aspx : "CHECK이 새로운 제약 조건에 대한 가정 및 WITH NOCHECK가 다시 활성화 제약 조건에 대한 가정으로 지정하지 않으면."
Zain Rizvi

8
@ZainRizvi : 새로운 데이터가 아니라 새로운 제약. 구속 조건을 비활성화 한 ALTER TABLE foo NOCHECK CONSTRAINT fk_b다음 다시 활성화 ALTER TABLE foo CHECK CONSTRAINT fk_b하면 구속 조건을 확인하지 않습니다. ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_b데이터를 확인하려면 필요합니다.
jmoreno

2
처음에 이것을 읽는 것이 분명하지 않았습니다. 두 번째 (중복) 라인은 구속 조건을 설정하는 기능입니다. 구속 조건은 기본적으로 켜져 있으므로 두 번째 줄은 중복됩니다.
blindguy

47

이것이 어떻게 작동하는지 보여주기 위해-

CREATE TABLE T1 (ID INT NOT NULL, SomeVal CHAR(1));
ALTER TABLE T1 ADD CONSTRAINT [PK_ID] PRIMARY KEY CLUSTERED (ID);

CREATE TABLE T2 (FKID INT, SomeOtherVal CHAR(2));

INSERT T1 (ID, SomeVal) SELECT 1, 'A';
INSERT T1 (ID, SomeVal) SELECT 2, 'B';

INSERT T2 (FKID, SomeOtherVal) SELECT 1, 'A1';
INSERT T2 (FKID, SomeOtherVal) SELECT 1, 'A2';
INSERT T2 (FKID, SomeOtherVal) SELECT 2, 'B1';
INSERT T2 (FKID, SomeOtherVal) SELECT 2, 'B2';
INSERT T2 (FKID, SomeOtherVal) SELECT 3, 'C1';  --orphan
INSERT T2 (FKID, SomeOtherVal) SELECT 3, 'C2';  --orphan

--Add the FK CONSTRAINT will fail because of existing orphaned records
ALTER TABLE T2 ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID);   --fails

--Same as ADD above, but explicitly states the intent to CHECK the FK values before creating the CONSTRAINT
ALTER TABLE T2 WITH CHECK ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID);    --fails

--Add the CONSTRAINT without checking existing values
ALTER TABLE T2 WITH NOCHECK ADD CONSTRAINT FK_T2_T1 FOREIGN KEY (FKID) REFERENCES T1 (ID);  --succeeds
ALTER TABLE T2 CHECK CONSTRAINT FK_T2_T1;   --succeeds since the CONSTRAINT is attributed as NOCHECK

--Attempt to enable CONSTRAINT fails due to orphans
ALTER TABLE T2 WITH CHECK CHECK CONSTRAINT FK_T2_T1;    --fails

--Remove orphans
DELETE FROM T2 WHERE FKID NOT IN (SELECT ID FROM T1);

--Enabling the CONSTRAINT succeeds
ALTER TABLE T2 WITH CHECK CHECK CONSTRAINT FK_T2_T1;    --succeeds; orphans removed

--Clean up
DROP TABLE T2;
DROP TABLE T1;

7
정리 DROP TABLE T2; DROP TABLE T1;
Graeme

8
나는 밤마다 복사하여 붙여 넣기를 돕기 위해 귀하의 의견에 대한 정리 코드를 실제 답변에 추가했습니다.
mwolfe02

18
"야간 복사하여 붙여 넣기"는 약간 부정적으로 보입니다. "이러한 유형의 자세한 예제를 매우 귀중하게 생각하는 스택 사용자 중 한 명이라고 생각합니다."
GaTechThomas

2
'밤에 날아 다니는 것'이란 말이 저를 완벽하게 묘사하는 것처럼 느껴집니다.
sanepete

21

신뢰할 수있는 제약 조건에 대한 위의 우수한 의견 외에도 :

select * from sys.foreign_keys where is_not_trusted = 1 ;
select * from sys.check_constraints where is_not_trusted = 1 ;

이름에서 알 수 있듯이 신뢰할 수없는 제약 조건은 현재 테이블의 데이터 상태를 정확하게 나타 내기 위해 신뢰할 수 없습니다. 그러나 향후 추가 및 수정 된 데이터를 확인하는 것은 신뢰할 수 있습니다.

또한 쿼리 최적화 프로그램에서는 신뢰할 수없는 제약 조건을 무시합니다.

검사 제한 조건 및 외래 키 제한 조건을 활성화하는 코드는 "check"라는 단어의 세 가지 의미로 상당히 나쁩니다.

ALTER TABLE [Production].[ProductCostHistory] 
WITH CHECK -- This means "Check the existing data in the table".
CHECK CONSTRAINT -- This means "enable the check or foreign key constraint".
[FK_ProductCostHistory_Product_ProductID] -- The name of the check or foreign key constraint, or "ALL".

15

WITH NOCHECK 정의 된 제약 조건을 준수하지 않는 테이블에 기존 데이터가 있고 구현하는 새 제약 조건을 무시하고 실행하지 않으려는 경우에도 사용됩니다 ...


13

WITH CHECK 실제로 기본 동작이지만 코딩에 포함시키는 것이 좋습니다.

대체 행동은 물론 사용하기 WITH NOCHECK때문에 의도를 명시 적으로 정의하는 것이 좋습니다. 인라인 파티션으로 재생 / 수정 / 전환 할 때 자주 사용됩니다.


9

외래 키 및 검사 제약 조건은 활성화 또는 비활성화 될뿐만 아니라 신뢰할 수 있거나 신뢰할 수없는 개념입니다. 자세한 내용은 MSDN 페이지를 참조 ALTER TABLE하십시오.

WITH CHECK새 외래 키 및 검사 제약 조건을 추가하기위한 기본값이고, WITH NOCHECK비활성화 된 외래 키 및 검사 제약 조건을 다시 활성화하기위한 기본값입니다. 차이점을 인식하는 것이 중요합니다.

그럼에도 불구하고, 유틸리티에 의해 생성 된 명백하게 중복 된 진술은 단순히 안전 및 / 또는 코딩 용이성을 위해 존재한다. 걱정하지 마십시오.


이 링크가 참조하고 있습니까? msdn.microsoft.com/en-us/library/ms190273.aspx ? 이것은 각 제약 조건에 대해 수행하는 대신 CHECK CHECK CONSTRAINT ALL을 사용하여 ALTER TABLE 테이블을 수행해야 함을 의미합니까?
Henrik Staun Poulsen

@HenrikStaunPoulsen : 그렇습니다. 각 제약 조건을 개별적으로 활성화하는 것을 막을 수는 없지만 WITH CHECK CHECK CONSTRAINT신뢰할 수 있다고 말해야 합니다.
Christian Hayter

나는 그것을 시도했다; "ALTER TABLE [dfm]. [TRATransformError] with CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent]"를 실행했습니다. 그러나 FK는 여전히 Is_Not_Trusted = 1입니다. 그런 다음 FK를 삭제하고 "WITH CHECK CHECK"로 다시 만들었습니다. 이제 Is_Not_Trusted = 0입니다. 마침내. 왜 그런지 아십니까? 나는 항상 is_not_for_replication = 0을 가지고 있음에 유의하십시오
Henrik

@HenrikStaunPoulsen : 잘 모르겠습니다. 항상 나에게 잘 작동했습니다. select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1)비활성화 및 신뢰할 수없는 제약 조건을 찾는 것과 같은 쿼리를 실행합니다 . 위와 같이 적절한 alter table 문을 실행하면 해당 제약 조건이 쿼리에서 사라 지므로 작동하는 것을 볼 수 있습니다.
Christian Hayter

2
@HenrikStaunPoulsen은 not_for_replication 플래그가 1로 설정 되었기 때문에 제약 조건을 신뢰할 수없는 것으로 만듭니다. SELECT 이름, create_date, modify_date, is_disabled, is_not_for_replication, is_not_trusted FROM sys.foreign_keys WHERE is_not_trusted = 1이 경우 제약 조건을 삭제하고 다시 만들어야합니다. gist.github.com/smoothdeveloper/ea48e43aead426248c0f 를 달성하기 위해 이것을 사용 합니다. 삭제시 및 업데이트 시이 스크립트에 지정되어 있지 않으므로이를 고려해야합니다.
kuklei

8

다음은 데이터베이스에서 신뢰할 수없는 제약 조건을 식별하고 수정하는 데 도움이되도록 작성한 코드입니다. 각 문제를 해결하는 코드를 생성합니다.

    ;WITH Untrusted (ConstraintType, ConstraintName, ConstraintTable, ParentTable, IsDisabled, IsNotForReplication, IsNotTrusted, RowIndex) AS
(
    SELECT 
        'Untrusted FOREIGN KEY' AS FKType
        , fk.name AS FKName
        , OBJECT_NAME( fk.parent_object_id) AS FKTableName
        , OBJECT_NAME( fk.referenced_object_id) AS PKTableName 
        , fk.is_disabled
        , fk.is_not_for_replication
        , fk.is_not_trusted
        , ROW_NUMBER() OVER (ORDER BY OBJECT_NAME( fk.parent_object_id), OBJECT_NAME( fk.referenced_object_id), fk.name) AS RowIndex
    FROM 
        sys.foreign_keys fk 
    WHERE 
        is_ms_shipped = 0 
        AND fk.is_not_trusted = 1       

    UNION ALL

    SELECT 
        'Untrusted CHECK' AS KType
        , cc.name AS CKName
        , OBJECT_NAME( cc.parent_object_id) AS CKTableName
        , NULL AS ParentTable
        , cc.is_disabled
        , cc.is_not_for_replication
        , cc.is_not_trusted
        , ROW_NUMBER() OVER (ORDER BY OBJECT_NAME( cc.parent_object_id), cc.name) AS RowIndex
    FROM 
        sys.check_constraints cc 
    WHERE 
        cc.is_ms_shipped = 0
        AND cc.is_not_trusted = 1

)
SELECT 
    u.ConstraintType
    , u.ConstraintName
    , u.ConstraintTable
    , u.ParentTable
    , u.IsDisabled
    , u.IsNotForReplication
    , u.IsNotTrusted
    , u.RowIndex
    , 'RAISERROR( ''Now CHECKing {%i of %i)--> %s ON TABLE %s'', 0, 1' 
        + ', ' + CAST( u.RowIndex AS VARCHAR(64))
        + ', ' + CAST( x.CommandCount AS VARCHAR(64))
        + ', ' + '''' + QUOTENAME( u.ConstraintName) + '''' 
        + ', ' + '''' + QUOTENAME( u.ConstraintTable) + '''' 
        + ') WITH NOWAIT;'
    + 'ALTER TABLE ' + QUOTENAME( u.ConstraintTable) + ' WITH CHECK CHECK CONSTRAINT ' + QUOTENAME( u.ConstraintName) + ';' AS FIX_SQL
FROM Untrusted u
CROSS APPLY (SELECT COUNT(*) AS CommandCount FROM Untrusted WHERE ConstraintType = u.ConstraintType) x
ORDER BY ConstraintType, ConstraintTable, ParentTable;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.