为什么这个带有FOREIGN KEY hack的T-SQL OUTPUT INTO有效?

时间:2015-03-16 22:07:57

标签: sql-server tsql foreign-keys

基础示例取自No way to use TSQL Output with normal foreign key constraints?;他的代码按预期失败了。但是,如果在示例中定义约束的方式修改如下,定义FK约束WITH NOCHECK然后CHECK,则OUTPUT INTO将无阻碍地运行。

这似乎与OUTPUT clause docs相矛盾。具体来说:

  

output_table [接收INTO的表格]不能:

     

•已启用在其上定义的触发器。

     

参与FOREIGN KEY约束的任何一方 [强调添加]。

     

•具有CHECK约束或启用规则。

从关系角度来看,下面的可以工作,但应该特别排除此操作。如果将FK定义为直接“WITH CHECK”(默认值),则会按预期失败。如果它被定义为“WITH NOCHECK”,然后使用“CHECK CONSTRAINT”启用它,那么它就不会失败。

如果这是一个已知的支持功能,那将是非常棒的。或者我刚刚发现SQL中存在的错误至少是SQL 2008(我在2008年和2014年测试过)?为什么这样做?为什么不呢?使用它我有什么风险?

IF OBJECT_ID ('dbo.forn') IS NOT NULL
begin
    alter table dbo.forn drop constraint FK_forn_prim
    DROP TABLE dbo.forn;
end
IF OBJECT_ID ('dbo.prim') IS NOT NULL
    DROP TABLE dbo.prim;
go

CREATE TABLE dbo.prim (c1 int PRIMARY KEY);
CREATE TABLE dbo.forn (c1 int );
alter table dbo.forn with nocheck add CONSTRAINT FK_forn_prim FOREIGN KEY (c1) REFERENCES dbo.prim(c1);
alter table dbo.forn check CONSTRAINT FK_forn_prim ;
go

-- does in fact fail with foreign key constraint violation
insert dbo.forn values (2);

-- works!!
INSERT INTO dbo.prim
    OUTPUT inserted.c1 INTO dbo.forn
SELECT 1;

3 个答案:

答案 0 :(得分:3)

在with check语句之后,系统仍然不信任外键。这是因为现有约束的默认值为WITH NOCHECK,因此您可以有效地运行:

    alter table dbo.forn with nocheck check CONSTRAINT FK_forn_prim;

重新启用的正确语句是:

    alter table dbo.forn with check check CONSTRAINT FK_forn_prim;

嵌套插入在运行后会再次失败。不建议让FK不受信任,因为SQL不会考虑许多操作。

您可以在系统视图sys.foreign_keys,is_not_trusted字段中检查不受信任的FK。

更多信息:https://sqlserverfast.com/blog/hugo/2007/03/can-you-trust-your-constraints/

答案 1 :(得分:0)

将您创建的值输出到第二个表中的语句可以正常工作,因为您的外键约束方向相反。内部语句依赖于外部语句,因此它将在另一个表中创建记录后执行。

只有在创建或更新记录dbo.forn时,SQL Server才会检查此外键。只要您在dbo.forn之前插入dbo.prim,您的代码就会执行。

使用:

INSERT INTO dbo.prim
    OUTPUT inserted.c1 INTO dbo.forn

失败:

INSERT INTO dbo.forn
    OUTPUT inserted.c1 INTO dbo.prim

<强> 编辑:

微软可能会选择快速记录可能是一个相当复杂的场景,说你不应该期望它能够工作。我敢打赌,基于OUTPUT [INTO]列出的具有UPDATE和DELETE语句的细节,使用UPDATE或DELETE的类似尝试将失败。

{INSERT,UPDATE,DELETE} * {约束类型} * {列的类型} * {触发类型} * {无论我错过了什么}变成了大量的文档组合。

像往常一样,经验证据胜出,你的情景是可能的。

答案 2 :(得分:-1)

非常确定答案是隐式事务和约束检查是在事务结束时

IF OBJECT_ID ('dbo.forn') IS NOT NULL
begin
    alter table dbo.forn drop constraint FK_forn_prim
    DROP TABLE dbo.forn;
end
IF OBJECT_ID ('dbo.prim') IS NOT NULL
    DROP TABLE dbo.prim;
go

CREATE TABLE dbo.prim (c1 int PRIMARY KEY);
CREATE TABLE dbo.forn (c1 int );
alter table dbo.forn with nocheck add CONSTRAINT FK_forn_prim FOREIGN KEY (c1) REFERENCES dbo.prim(c1);
alter table dbo.forn check CONSTRAINT FK_forn_prim ;
go

-- does in fact fail with foreign key constraint violation
insert dbo.forn values (1);

-- works!!
INSERT INTO dbo.prim
    OUTPUT inserted.c1 INTO dbo.forn
SELECT 2;

-- does in fact fail with foreign key constraint violation
INSERT INTO dbo.prim
    OUTPUT inserted.c1 + 1 INTO dbo.forn
SELECT 5;

select * from dbo.prim;  -- 2 insert - it really did work   
select * from dbo.forn;  -- 2 insert - it really did work