在触发器中,如何引用刚刚插入的行?

时间:2016-02-01 15:29:57

标签: sql sql-server tsql database-design

让我解释一下我的情况。我用

生成了3个表
CREATE TABLE Partners 
( 
    id INT IDENTITY(1,1),
    name NVARCHAR(50) NOT NULL,
    PRIMARY KEY (id)
);

-- snip ... 
CREATE TABLE Questions 
(
    id INT IDENTITY(1,1),
    section_id INT,
    qtext NVARCHAR(300) NOT NULL,
    PRIMARY KEY (id),
    FOREIGN KEY (section_id) REFERENCES Sections(id) ON DELETE CASCADE
);

-- snip ... 
CREATE TABLE Answers 
(
    id INT IDENTITY (1,1),
    question_id INT,
    partner_id INT,
    val DECIMAL DEFAULT 0.0,
    PRIMARY KEY (id),
    FOREIGN KEY (question_id) REFERENCES Questions(id) ON DELETE CASCADE,
    FOREIGN KEY (partner_id) REFERENCES Partners(id) ON DELETE CASCADE
);

我正在尝试设置一个触发器,以便在添加新合作伙伴时,为每个问题生成默认答案。

我尝试创建该触发器是

-- Create trigger so that adding a partner results in default
-- answers for every survey
-- See https://stackoverflow.com/questions/11852782/t-sql-loop-over-query-results
CREATE TRIGGER DefaultAnswers     
    ON  Partners       
    AFTER INSERT     
AS       
BEGIN    
    CREATE TABLE QuestIds (RowNum INT, Id INT);

    INSERT INTO QuestIds (RowNum, Id)
        SELECT DISTINCT ROW_NUMBER() OVER (ORDER BY Id) as RowNum, Id
        FROM TABLE

    DECLARE @id INT
    DECLARE @totalrows INT = (SELECT COUNT(*) FROM QuestIds)
    DECLARE @currentrow INT = 1

    WHILE @currentrow <  @totalrows  
    BEGIN 
        SET @id = (SELECT Id FROM QuestIds WHERE RowNum = @currentrow)

        EXEC AddAnswerWithoutVal @question_id=@id, @partner_id=INSERTED.id

        SET @currentrow = @currentrow +1
    END 
END

,错误是

  

Msg 156,Level 15,State 1,Procedure DefaultAnswers,Line 313
  关键字“TABLE”附近的语法不正确。

     

Msg 102,Level 15,State 1,Procedure DefaultAnswers,Line 323
  '。'。

附近的语法不正确
你可以帮我解决这个问题吗?我想,INSERTED指的是刚刚插入表中的行。至于FROM TABLE,这也是我试图扯掉T-SQL loop over query results的东西。

编辑:我有另一个问题。当我开始工作时,触发器是否会引用成功插入的行?我想确保这是一个原子操作。

3 个答案:

答案 0 :(得分:4)

使用存储过程插入行似乎是一个坏主意。也许由于某种原因它是必要的,但将它简单地表达为单个插入更容易。像这样:

insert into Answers(question_id, partner_id, val)
    select q.id, i.id, NULL
    from inserted i cross join
         questions q;

这将取代整个身体,不需要临时桌子。在触发器中创建表似乎是一个坏主意。

答案 1 :(得分:1)

语法错误的原因在于:

    INSERT INTO QuestIds (RowNum, Id)
    SELECT DISTINCT ROW_NUMBER() OVER (ORDER BY Id) as RowNum, Id
    FROM TABLE

您需要将表的名称放在FROM子句中,而不是关键字TABLE

但是你真的想在每次触发触发器时创建这个表吗?第二次触发并且表已经存在会发生什么?

虽然我正在考虑这个问题,但是:

  

我正在尝试设置触发器,以便在添加新合作伙伴时   为每个问题生成默认答案。

首先听起来像次优解决方案。如果为每个问题定义了默认答案,为什么不假设,只要伙伴错过了问题的答案,他的答案就是默认答案。然后,不要为新合作伙伴添加任何答案,直到稍后专门添加它们为止。

这是我要探索的替代方法:根本没有触发器。

答案 2 :(得分:1)

好的,首先,你从来没有,我的意思是永远不想循环触发器。触发该循环可能会导致可怕的性能问题。您必须假设可能会插入或删除多个记录,并且永远不会假设只有一个记录。触发器必须兼顾两者。

是插入的表包含刚刚插入的记录。然后,您将像处理任何其他表一样处理它们。你所写的内容也不适用于任何普通的表格。您无法在EXEC语句中引用表。

您需要做的第一件事就是重写proc,以便它根据数据集而不是单个记录进行处理。再次,尝试循环记录是一件非常糟糕的事情。事实上,我根本不会通过另一个存储过程执行此操作,而是将所有逻辑放在触发器中,除非有其他进程使用proc。但是,任何情况下的逻辑都需要设置为基础,而不是一次一行或一次记录。