MIT CHECK ADD CONSTRAINT gefolgt von CHECK CONSTRAINT vs. ADD CONSTRAINT

133

Ich schaue mir die AdventureWorks-Beispieldatenbank für SQL Server 2008 an und sehe in ihren Erstellungsskripten, dass sie normalerweise Folgendes verwenden:

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

gefolgt von:

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

Ich sehe dies für Fremdschlüssel (wie hier), eindeutige Einschränkungen und reguläre CHECKEinschränkungen. DEFAULTEinschränkungen verwenden das reguläre Format, mit dem ich besser vertraut bin, wie z.

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

Was ist der Unterschied zwischen dem ersten und dem zweiten Weg?

Wayne Molina
quelle

Antworten:

94

Die erste Syntax ist redundant - WITH CHECK ist die Standardeinstellung für neue Einschränkungen, und die Einschränkung ist standardmäßig ebenfalls aktiviert.

Diese Syntax wird vom SQL Management Studio beim Generieren von SQL-Skripten generiert. Ich gehe davon aus, dass es sich um eine zusätzliche Redundanz handelt, möglicherweise um sicherzustellen, dass die Einschränkung aktiviert ist, auch wenn das Standardverhalten für Einschränkungen für eine Tabelle geändert wird.

Chris Hynes
quelle
12
Es sieht nicht so aus, als ob WITH CHECK tatsächlich die Standardeinstellung ist, sondern nur die Standardeinstellung für neue Daten. Von msdn.microsoft.com/en-us/library/ms190273.aspx : "Wenn nicht angegeben, wird WITH CHECK für neue Einschränkungen und WITH NOCHECK für wieder aktivierte Einschränkungen angenommen."
Zain Rizvi
8
@ZainRizvi: keine neuen Daten, neue Einschränkungen. Wenn Sie eine Einschränkung mit deaktivieren ALTER TABLE foo NOCHECK CONSTRAINT fk_bund dann wieder aktivieren ALTER TABLE foo CHECK CONSTRAINT fk_b, wird die Einschränkung nicht überprüft. ALTER TABLE foo WITH CHECK CHECK CONSTRAINT fk_bist notwendig, um die Daten überprüfen zu lassen.
jmoreno
2
Es war mir anfangs nicht klar, dies zu lesen. Die zweite (redundante) Zeile ist die Funktion zum Aktivieren der Einschränkung. Da die Einschränkung standardmäßig aktiviert ist, ist die zweite Zeile redundant.
Blindguy
47

Um zu demonstrieren, wie das funktioniert--

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;
Graeme
quelle
7
Aufräumen - DROP TABLE T2; DROP TABLE T1;
Graeme
8
Ich habe den Bereinigungscode aus Ihrem Kommentar zu Ihrer tatsächlichen Antwort hinzugefügt, um die Fly-by-Night-Copy-and-Pasters da draußen zu unterstützen.
mwolfe02
18
"Fly-by-Night Copy-and-Pasters" wirkt etwas negativ. Ich würde mich als einen der Stack-Benutzer betrachten (für eine positivere Formulierung ...), "die diese Art von detaillierten Beispielen äußerst wertvoll finden".
GaTechThomas
2
Abfällig oder nicht, "Fly by Night" fühlt sich so an, als würde es mich perfekt beschreiben.
Sanepete
21

Weiter zu den oben genannten ausgezeichneten Kommentaren zu vertrauenswürdigen Einschränkungen:

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

Einer nicht vertrauenswürdigen Einschränkung kann, wie der Name schon sagt, nicht vertraut werden, um den aktuellen Status der Daten in der Tabelle genau darzustellen. Es kann jedoch vertrauenswürdig sein, Daten zu überprüfen, die in Zukunft hinzugefügt und geändert werden.

Darüber hinaus werden nicht vertrauenswürdige Einschränkungen vom Abfrageoptimierer ignoriert.

Der Code zum Aktivieren von Prüf- und Fremdschlüsseleinschränkungen ist ziemlich schlecht, mit drei Bedeutungen des Wortes "Prüfung".

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".
Greenstone Walker
quelle
15

WITH NOCHECK wird auch verwendet, wenn in einer Tabelle vorhandene Daten vorhanden sind, die nicht der definierten Einschränkung entsprechen, und Sie nicht möchten, dass sie der neuen Einschränkung, die Sie implementieren, zuwiderlaufen ...

Mittag und
quelle
13

WITH CHECK ist in der Tat das Standardverhalten, es wird jedoch empfohlen, es in Ihre Codierung aufzunehmen.

Das alternative Verhalten ist natürlich zu verwenden WITH NOCHECK, daher ist es gut, Ihre Absichten explizit zu definieren. Dies wird häufig verwendet, wenn Sie mit Inline-Partitionen spielen / diese ändern / wechseln.

John Sansom
quelle
9

Fremdschlüssel- und Prüfbeschränkungen haben das Konzept, vertrauenswürdig oder nicht vertrauenswürdig zu sein sowie aktiviert und deaktiviert zu werden. ALTER TABLEAusführliche Informationen finden Sie auf der MSDN-Seite .

WITH CHECKist die Standardeinstellung zum Hinzufügen neuer Fremdschlüssel- und Überprüfungsbeschränkungen, WITH NOCHECKdie Standardeinstellung zum erneuten Aktivieren deaktivierter Fremdschlüssel- und Überprüfungsbeschränkungen. Es ist wichtig, sich des Unterschieds bewusst zu sein.

Allerdings dienen scheinbar redundante Anweisungen, die von Versorgungsunternehmen generiert werden, lediglich der Sicherheit und / oder der einfachen Codierung. Mach dir keine Sorgen um sie.

Christian Hayter
quelle
Ist dies der Link, auf den Sie sich beziehen: msdn.microsoft.com/en-us/library/ms190273.aspx ? Bedeutet dies, dass wir eine ALTER TABLE-Tabelle mit CHECK CHECK CONSTRAINT ALL erstellen müssen, anstatt dies für jede Einschränkung zu tun?
Henrik Staun Poulsen
@ HenrikStaunPoulsen: Ja das ist der Link. Nichts hindert Sie daran, jede Einschränkung einzeln zu aktivieren, aber Sie müssen sagen WITH CHECK CHECK CONSTRAINT, dass Sie ihnen vertrauen können.
Christian Hayter
Das habe ich versucht; Ich habe "ALTER TABLE [dfm]. [TRATransformError] WITH CHECK CHECK CONSTRAINT [FK_TRATransformError_ETLEvent]" ausgeführt. Aber der FK hat immer noch Is_Not_Trusted = 1. Dann habe ich den FK gelöscht und ihn mit "WITH CHECK CHECK" neu erstellt, und jetzt habe ich Is_Not_Trusted = 0. Zu guter Letzt. Weißt du, warum? Bitte beachten Sie, dass ich immer is_not_for_replication = 0
Henrik Staun Poulsen
@ HenrikStaunPoulsen: Ich weiß nicht, es hat immer gut für mich funktioniert. Ich führe eine Abfrage aus select * from sys.objects where [type] in ('C', 'F') and (objectproperty([object_id], 'CnstIsDisabled') = 1 or objectproperty([object_id], 'CnstIsNotTrusted') = 1), um deaktivierte und nicht vertrauenswürdige Einschränkungen zu finden. Nach dem Ausgeben der entsprechenden alter table-Anweisungen wie oben verschwinden diese Einschränkungen aus der Abfrage, sodass ich sehen kann, dass sie funktioniert.
Christian Hayter
2
@HenrikStaunPoulsen, weil das Flag not_for_replication auf 1 gesetzt ist. Dadurch wird die Einschränkung als nicht vertrauenswürdig eingestuft. SELECT name, create_date, modify_date, is_disabled, is_not_for_replication, is_not_trusted FROM sys.foreign_keys WHERE is_not_trusted = 1 In diesem Fall müssen Sie die Einschränkung löschen und neu erstellen. Ich benutze dies, um dies zu erreichen. Gist.github.com/smoothdeveloper/ea48e43aead426248c0f Beachten Sie, dass beim Löschen und Aktualisieren in diesem Skript keine Angaben gemacht werden und Sie dies berücksichtigen müssen.
Kuklei
8

Hier ist ein Code, den ich geschrieben habe, um nicht vertrauenswürdige Einschränkungen in einer Datenbank zu identifizieren und zu korrigieren. Es generiert den Code zur Behebung jedes Problems.

    ;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;
Graeme
quelle