使用存储过程调用的 BEFORE INSERT 触发器 (DB2 LUW 9.5)

发布于 2024-10-20 20:32:24 字数 1259 浏览 1 评论 0原文

我正在尝试创建一个 BEFORE INSERT 触发器,该触发器将检查字段的传入值,如果该字段为空,则将其替换为另一行中的相同字段。但是,当我将 CALL 语句添加到触发器时,会返回错误“触发器“ORGSTRUCT.CSTCNTR_IN”是使用不受支持的触发 SQL 语句定义的”。我检查了文档,发现之前不支持游标(首先创建存储过程的部分原因),但即使我从存储过程中删除游标声明,调用仍然会生成相同的错误。

触发器:

CREATE TRIGGER orgstruct.cstcntr_IN
        NO CASCADE
        BEFORE INSERT ON orgstruct.tOrgs
        REFERENCING NEW AS r
        FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
    DECLARE prnt_temp BIGINT;
    DECLARE cstcntr_temp CHAR(11);

    SET prnt_temp = r.prnt;
    SET cstcntr_temp = r.cstcntr;

    CALL orgstruct.trspGetPrntCstCntr(prnt_temp,cstcntr_temp);
    SET r.cstcntr = cstcntr_temp;
END

存储过程:

CREATE PROCEDURE orgstruct.trspGetPrntCstCntr (
    IN  p_prnt              BIGINT,
    OUT p_cstcntr       CHAR(11)
)
SPECIFIC trGetPrntCstCntr
BEGIN
    IF p_prnt IS NULL THEN
        RETURN;
    END IF;

    BEGIN
        DECLARE c1 CURSOR
            FOR
                SELECT cstcntr
                FROM orgstruct.tOrgs
                WHERE id = p_prnt
            FOR READ ONLY;
        OPEN c1;
        FETCH FROM c1 INTO p_cstcntr;
        CLOSE c1;
    END;
END

根据文档,BEFORE触发器中允许CALL,所以我不明白问题是什么。

I am trying to create a BEFORE INSERT trigger that will check the incoming value of a field, and replace it with the same field in another row if that the field is null. However, when I add the CALL statement to my trigger, an error is returned "The trigger "ORGSTRUCT.CSTCNTR_IN" is defined with an unsupported triggered SQL statement". I checked the documentation and saw that cursors weren't supported in the BEFORE (part of the reason for making the stored procedure in the first place), but even when I remove the cursor declaration from the stored procedure the call still generates the same error.

Trigger:

CREATE TRIGGER orgstruct.cstcntr_IN
        NO CASCADE
        BEFORE INSERT ON orgstruct.tOrgs
        REFERENCING NEW AS r
        FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
    DECLARE prnt_temp BIGINT;
    DECLARE cstcntr_temp CHAR(11);

    SET prnt_temp = r.prnt;
    SET cstcntr_temp = r.cstcntr;

    CALL orgstruct.trspGetPrntCstCntr(prnt_temp,cstcntr_temp);
    SET r.cstcntr = cstcntr_temp;
END

Stored procedure:

CREATE PROCEDURE orgstruct.trspGetPrntCstCntr (
    IN  p_prnt              BIGINT,
    OUT p_cstcntr       CHAR(11)
)
SPECIFIC trGetPrntCstCntr
BEGIN
    IF p_prnt IS NULL THEN
        RETURN;
    END IF;

    BEGIN
        DECLARE c1 CURSOR
            FOR
                SELECT cstcntr
                FROM orgstruct.tOrgs
                WHERE id = p_prnt
            FOR READ ONLY;
        OPEN c1;
        FETCH FROM c1 INTO p_cstcntr;
        CLOSE c1;
    END;
END

According to the documentation, CALL is allowed in a BEFORE trigger, so I don't understand what the problem is.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

﹏半生如梦愿梦如真 2024-10-27 20:32:24

before 触发器可以调用存储过程,但存储过程不能执行触发器中不允许的任何操作。

在您的情况下,SQL 存储过程的默认数据访问级别是 MODIFIES SQL DATA,这在触发器中是不允许的。您可以重新创建存储过程,将数据访问级别更改为“READS SQL DATA”;这将允许您创建触发器。

但是:没有理由为如此简单的事情调用存储过程;您可以使用简单的内联触发器来完成此操作:

create trigger orgstruct.cstcntr_IN
   no cascade
   before insert on orgstruct.tOrgs
   referencing new as r
   for each row
   mode db2sql
   set r.cstcntr = case 
                     when r.p_prnt is not null 
                       then (select cstcntr from tOrgs where id = r.p_prnt fetch first 1 row only) 
                     else r.cstcntr 
                   end;

这将更加高效,因为它消除了存储过程调用和存储过程内的游标处理。即使您想使用存储过程,也可以消除存储过程中的游标并提高性能。

仅供参考:您发布的逻辑包含错误,并且始终将 CSTCNTR 设置为 NULL。此答案中发布的触发器不会执行此操作。 :-)

A before trigger can call a stored procedure, but the stored proc can't do anything not allowed in the trigger.

In your case, the default level of data access for a SQL stored proc is MODIFIES SQL DATA, which is not allowed in the trigger. You could recreate your stored procedure, changing the data access level to READS SQL DATA; this will allow you to create the trigger.

However: There is no reason to call a stored procedure for something this simple; You can do it using a simple inline trigger:

create trigger orgstruct.cstcntr_IN
   no cascade
   before insert on orgstruct.tOrgs
   referencing new as r
   for each row
   mode db2sql
   set r.cstcntr = case 
                     when r.p_prnt is not null 
                       then (select cstcntr from tOrgs where id = r.p_prnt fetch first 1 row only) 
                     else r.cstcntr 
                   end;

This will be a LOT more efficient because it eliminates both the stored procedure call and the cursor processing inside the stored proc. Even if you wanted to use the stored proc, you could eliminate the cursor inside the stored proc and improve performance.

FYI: the logic that you posted contains an error, and will always set CSTCNTR to NULL. The trigger posted in this answer not do this. :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文