为什么这个 T-SQL OUTPUT INTO with FOREIGN KEY hack 有效?
Why does this T-SQL OUTPUT INTO with FOREIGN KEY hack work?
基本示例来自No way to use TSQL Output with normal foreign key constraints?;他的代码按预期失败。但是如果将示例中定义约束的方式修改如下,定义FK约束WITH NOCHECK
然后CHECK
ing它,OUTPUT INTO将运行畅通无阻。
这似乎与OUTPUT clause docs矛盾。具体来说:
output_table [the table receiving the INTO] cannot:
•Have enabled triggers defined on it.
•Participate on either side of a FOREIGN KEY constraint [emphasis added].
•Have CHECK constraints or enabled rules.
从关系的角度来看,以下 可以 工作,但应该明确排除此操作。如果 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;
您将创建的值输出到第二个 table 的语句有效,因为您的外键约束方向相反。内部语句依赖于外部语句,因此它将在另一个 table.
中创建记录后执行
SQL 服务器只会在创建或更新记录 dbo.forn 时检查此外键。只要您在 dbo.forn 之前插入 dbo.prim,您的代码就会执行。
作品:
INSERT INTO dbo.prim
OUTPUT inserted.c1 INTO dbo.forn
失败:
INSERT INTO dbo.forn
OUTPUT inserted.c1 INTO dbo.prim
编辑:
Microsoft 可能会选择快捷方式记录可能相当复杂的场景,因为您不应期望它会起作用。我敢打赌,根据为带有 UPDATE 和 DELETE 语句的 OUTPUT [INTO] 列出的细节,使用 UPDATE 或 DELETE 的类似尝试将失败。
{INSERT, UPDATE, DELETE} * {Types of Constraints} * {Types of Columns} * {Types of triggers} * {Whatever Else I Missed} 变成了大量需要记录的组合。
像往常一样,经验证据获胜,您的情景是可能的。
很确定答案是隐式事务并且约束检查在事务结束时进行
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
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 字段中检查不受信任的外键。
更多信息:https://sqlserverfast.com/blog/hugo/2007/03/can-you-trust-your-constraints/
基本示例来自No way to use TSQL Output with normal foreign key constraints?;他的代码按预期失败。但是如果将示例中定义约束的方式修改如下,定义FK约束WITH NOCHECK
然后CHECK
ing它,OUTPUT INTO将运行畅通无阻。
这似乎与OUTPUT clause docs矛盾。具体来说:
output_table [the table receiving the INTO] cannot:
•Have enabled triggers defined on it.
•Participate on either side of a FOREIGN KEY constraint [emphasis added].
•Have CHECK constraints or enabled rules.
从关系的角度来看,以下 可以 工作,但应该明确排除此操作。如果 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;
您将创建的值输出到第二个 table 的语句有效,因为您的外键约束方向相反。内部语句依赖于外部语句,因此它将在另一个 table.
中创建记录后执行SQL 服务器只会在创建或更新记录 dbo.forn 时检查此外键。只要您在 dbo.forn 之前插入 dbo.prim,您的代码就会执行。
作品:
INSERT INTO dbo.prim
OUTPUT inserted.c1 INTO dbo.forn
失败:
INSERT INTO dbo.forn
OUTPUT inserted.c1 INTO dbo.prim
编辑:
Microsoft 可能会选择快捷方式记录可能相当复杂的场景,因为您不应期望它会起作用。我敢打赌,根据为带有 UPDATE 和 DELETE 语句的 OUTPUT [INTO] 列出的细节,使用 UPDATE 或 DELETE 的类似尝试将失败。
{INSERT, UPDATE, DELETE} * {Types of Constraints} * {Types of Columns} * {Types of triggers} * {Whatever Else I Missed} 变成了大量需要记录的组合。
像往常一样,经验证据获胜,您的情景是可能的。
很确定答案是隐式事务并且约束检查在事务结束时进行
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
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 字段中检查不受信任的外键。
更多信息:https://sqlserverfast.com/blog/hugo/2007/03/can-you-trust-your-constraints/