【问题标题】:declare and call variable in trigger in oracle在 oracle 的触发器中声明和调用变量
【发布时间】:2019-03-26 11:09:09
【问题描述】:

我正在尝试声明一个变量并再次访问它以更新特定 ID 的同一个表。(将 STATUS'E' 更新为 'R'

create or replace trigger resume_trgr
before update on temp_jobs

DECLARE
job_status varchar(2);

begin
  select STATUS into :job_status from temp_jobs where id=6120;
  if :job_status='E'
  then
    update temp_jobs set STATUS='R' where id=6120;
  end if;
end;
/

但在执行上述代码时返回错误。错误代码如下:

触发 resume_trgr 编译

 Errors: check compiler log

 Error(5,27): PLS-00049: bad bind variable 'STATUS'

【问题讨论】:

  • 有一个语句级触发器命中同一个表上的特定行似乎很奇怪(它会再次触发内部更新,首先);但在任何情况下,错误都不会与代码匹配。在您发布的内容中,job_status 是局部变量而不是绑定变量,因此不应有冒号前缀。但不清楚你真正在做什么或看到什么......

标签: oracle plsql database-trigger


【解决方案1】:

在你说的问题中,错误是指STATUS,但这与你发布的代码不匹配你应该得到两个错误,指的是JOB_STATUS

那是因为它被定义为局部变量,而您试图将其称为绑定变量。它不应该有冒号前缀;所以编译:

create or replace trigger resume_trgr
before update on temp_jobs
declare
  job_status temp_jobs.status%type;
begin
  select STATUS into job_status from temp_jobs where id=6120;
  if job_status='E'
  then
    update temp_jobs set STATUS='R' where id=6120;
  end if;
end;
/

这样做,完全跳过变量:

create or replace trigger resume_trgr
before update on temp_jobs
begin
  update temp_jobs set STATUS='R' where id=6120 and status = 'E';
end;
/

在触发器中做这件事是否有用、有效或明智是另一回事。这似乎是一个单独的更新,您应该在您的“真实”更新之前手动进行,可能是在一个过程中 - 如果您真的想始终更新该特定 ID 的行,而不管您在“真实”中实际做了什么(触发)更新。

如果您真正想做的是确保状态更改为R,即使调用者没有明确地这样做,那么您可能需要一个设置伪行值的行级触发器,而不是具有硬编码 ID 的语句级触发器。

【讨论】:

  • 谢谢亚历克斯。我试过你的建议。但它给出了两个不同的错误。一个是table is mutating, trigger/function may not see it另一个是(在我做了一些更改之后)It firing trigger in loop undefined times.
  • 是的。我仍然认为你真的想要一个行级触发器,但不清楚 - 特别是为什么你有一个硬编码的 ID。
【解决方案2】:

你得到的错误是……嗯,很奇怪。几个cmets:

  • 您的代码中没有变量 STATUS
  • 选择变量时,不需要冒号(即不是into :job_status,而是into job_status)。
  • 您应该使用VARCHAR2 数据类型,而不是VARCHAR
  • 这应该是一个行级触发器(所以你错过了FOR EACH ROW),一旦你解决了所有问题,你就会遇到mutating table error
  • 为什么要硬编码 ID?这真的会为那个 ID 做某事吗?

你可能会这样做:首先,测试用例:

SQL> create table temp_jobs
  2    (id number,
  3     status varchar2(10));

Table created.

SQL> insert into temp_jobs values (1, 'E');

1 row created.

SQL> insert into temp_jobs values (6120, 'E');

1 row created.

SQL> select * from temp_jobs;

        ID STATUS
---------- ----------
         1 E
      6120 E

触发器:注意你和我的代码之间的差异。

SQL> create or replace trigger resume_trgr
  2    before update on temp_jobs
  3    for each row
  4  DECLARE
  5    JOB_STATUS VARCHAR2(2);
  6  begin
  7    :new.status := case when :old.status = 'E' then 'R'
  8                        else :new.status
  9                   end;
 10  end;
 11  /

Trigger created.

测试:

SQL> update temp_jobs set status = 'Y' where id = 6120;

1 row updated.

SQL> select * from temp_jobs;

        ID STATUS
---------- ----------
         1 E
      6120 R

SQL> update temp_jobs set status = 'Y' where id = 6120;

1 row updated.

SQL> select * from temp_jobs;

        ID STATUS
---------- ----------
         1 E
      6120 Y

SQL>

【讨论】:

  • 我正在尝试使用 where 子句重置(从 E 到 R)特定行的值。我希望上面的代码会考虑所有值正在更新的行。
  • 应该(包括 ID = 6120)。
猜你喜欢
  • 2013-07-20
  • 2017-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-20
  • 1970-01-01
相关资源
最近更新 更多