无法使用具有正常外键约束的TSQL输出?

时间:2010-07-27 14:55:17

标签: tsql

以下代码段失败并显示错误:

  

OUTPUT INTO子句的目标表'dbo.forn'不能位于(主键,外键)关系的任何一侧。找到参考约束'FK_forn_prim'。“

我只能通过禁用外键约束来使用输出?怎么办呢?

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 CONSTRAINT FK_forn_prim FOREIGN KEY (c1) REFERENCES dbo.prim(c1));
go

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

5 个答案:

答案 0 :(得分:13)

通常输出到表变量或临时表,然后使用它来插入最终表。

答案 1 :(得分:2)

根据technet

  

无法保证更改应用于表的顺序以及将行插入输出表或表变量的顺序将对应。

基于此,存在许多限制,其中包括您在上面遇到的限制,但如果您只想在第二个表中记录插入的值,我不明白为什么您需要外键你在上面定义的关系。

如果确实需要外键关系,可以使用这样的触发器完成相同的操作。

    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 CONSTRAINT FK_forn_prim FOREIGN KEY (c1) REFERENCES dbo.prim(c1));
go

CREATE TRIGGER InsertRecord
   ON  dbo.Prim
   AFTER Insert
AS 
BEGIN
    SET NOCOUNT ON;
    Insert into dbo.forn Select * from inserted;
END
GO

INSERT INTO dbo.prim SELECT 1;

答案 2 :(得分:2)

你是对的,输出语句不适用于外键约束下的表。

另一种选择是在插入数据时临时禁用约束。虽然这不应该是常态,但它适用于一次加载数据。

ALTER TABLE dbo.forn
NOCHECK CONSTRAINT FK_forn_prim
GO

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

ALTER TABLE dbo.forn
CHECK CONSTRAINT FK_forn_prim
GO

答案 3 :(得分:1)

文档确实指出输出表可能不参与外键约束(或检查约束,也不能在其上定义启用的触发器 - 但这里不考虑这些情况)。 Msg 332错误就是其中的表现。

这不推荐。 请参阅下面的更新)

但是,我发现如果禁用外键约束并使用NOCHECK/CHECK 重新启用,即使重新启用约束,也会破坏此限制。换句话说:一个禁用启用周期足以使FK约束对输出到外键约束禁止“不可见”。请注意以下修改:

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);
-- note change here:
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 ;
-- end change
go

INSERT INTO dbo.prim
    OUTPUT inserted.c1 INTO dbo.forn
select 1;

以下失败(约束违规),因此您知道DRI仍在工作:

INSERT INTO dbo.prim
    OUTPUT inserted.c1 + 1 INTO dbo.forn
select 2;

鉴于文件中的禁令,我认为这似乎是可疑的。我在这一点上有question等待,但它没有受到好评。

更新

前面提到的问题是answered,新信息使这个答案无效。将此作为对他人的警告,以防他们偶然发现这个看似偶然的漏洞。

结果是:alter TABLE dbo.forn check CONSTRAINT FK_forn_prim ;重新启用约束但使其处于“不受信任”状态,因此SQL引擎无法完全利用它来进行索引优化等。不推荐。重新启用的正确方法是

alter table dbo.forn with check check CONSTRAINT FK_forn_prim;

答案 4 :(得分:0)

使用插入表变量
声明@HISTORY_EXTENSION 表
(
HISTORY_ID INT 非空,
NUMBER NVARCHAR(50) 非空
)

-- 移动自
插入 [dbo].[历史]
([代码]
,[邮票]
)
OUTPUT INSERTED.Id, 日期
INTO @HISTORY_EXTENSION (
[HISTORY_ID]
,[NUMBER]
)
选择
1、
日期
来自交易
WHERE 日期 = '2021-05-20'