使用检查添加约束,然后select检查约束与添加约束
我正在查看SQL Server 2008的AdventureWorks示例数据库,并在创build脚本中看到他们倾向于使用以下内容:
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
第一种方式与第二种方式之间有什么不同?
第一种语法是多余的 – WITH CHECK是新约束的默认值,约束在默认情况下也是打开的。
这个语法是由SQLpipe理工作室在生成sql脚本时生成的 – 我假设它是某种额外的冗余,可能是为了确保约束被启用,即使表的默认约束行为被改变。
为了演示这是如何工作的 –
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;
除了以上关于可信约束的优秀评论:
select * from sys.foreign_keys where is_not_trusted = 1 ; select * from sys.check_constraints where is_not_trusted = 1 ;
一个不可信的约束,正如它的名字所暗示的那样,不能被信任来正确地表示数据在表中的状态。 但是,它可以被信任来检查将来添加和修改的数据。
另外,查询优化器忽略不可信约束。
启用检查约束和外键约束的代码非常糟糕,具有“检查”这个词的三个含义。
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".
WITH NOCHECK
也用于当一个表中存在的数据不符合定义的约束时,并且不希望它与您正在实施的新约束相冲突…
WITH CHECK
确实是默认的行为,但是在编码中包含这是一个很好的做法。
另一种行为当然是使用WITH NOCHECK
,所以明确地定义你的意图是很好的。 当您正在使用/修改/切换内联分区时,通常会使用这个选项。
外键和检查约束具有被信任或不可信的概念,以及被启用和禁用。 有关完整的详细信息,请参阅ALTER TABLE
的MSDN页面。
WITH CHECK
是添加新外键和检查约束的默认值, WITH NOCHECK
是重新启用禁用外键和检查约束的默认值。 了解其中的差异很重要。
话虽如此,为了安全和/或简单的编码,任何由公用设施产生的明显冗余的陈述都是简单的。 别担心。
以下是我写的一些代码,可以帮助我们识别和更正数据库中的不受信任的CONSTRAINT。 它生成代码来解决每个问题。
;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;