在触发器崩溃 Oracle Apex 中使用 pragma autonomous_transaction 立即执行 'alter trigger ... disable'
Execute immediate 'alter trigger ... disable' with pragma autonomous_transaction in a trigger crashes Oracle Apex
我正在尝试使用预连接反规范化对这两个表进行反规范化,并且必须添加所有需要的触发器。
表格基本上是这样的:
CLIENTS
Client_ID CHAR(13),
Client_Name VARCHAR2(20),
Client_Surname VARCHAR2 (30),
...
PRIMARY KEY (Client_ID)
ACCOUNTS
Account_ID VARCHAR(20),
...
Client_ID CHAR(13),
Client_Name VARCHAR2(20),
Client_Surname VARCHAR2(30),
PRIMARY KEY (ACCOUNT_ID),
FOREIGN KEY (Client_ID) REFERENCES CLIENTS(Client_ID)
我有两个触发器与我的问题相关;一个用于禁用 Client_Name 和 Client_Surname 的手动编辑,如下所示:
CREATE OR REPLACE TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE
BEFORE UPDATE OF CLIENT_NAME, CLIENT_SURNAME ON ACCOUNTS
FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR( NUM => -20002, MSG => 'Updating client_name and client_surname is not permitted!');
END;
第二个触发器用于在添加新帐户时自动插入客户端名称和姓氏,即连接到客户端。它看起来像这样:
CREATE OR REPLACE TRIGGER ACCOUNTS_INSERT_UPDATE
BEFORE INSERT OR UPDATE ON ACCOUNTS
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
new_name VARCHAR2(20);
new_surname VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE DISABLE';
INSERT Client_Name INTO new_name FROM CLIENTS
WHERE Client_ID = :NEW.Client_ID;
:NEW.Client_Name := new_name;
INSERT Client_Surname INTO new_surname FROM CLIENTS
WHERE Client_ID = :NEW.Client_ID;
:NEW.Client_Surname := new_surname;
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE ENABLE';
END;
而且有效!只要我删除 Execute immediate statements 和 pragma autonomous transaction(并手动禁用第一个触发器)。它达到了预期的效果 - 它用客户的名字和姓氏填充了行。
但是当我添加 pragma 自治事务并执行立即语句时,Apex Oracle(SQL 命令)页面冻结然后崩溃,我必须重新加载页面。
需要使用触发器来实现此效果,尽管它看起来确实不适合这项工作。
我使用的是最新版本的 Oracle Apex,在撰写此问题时为 Oracle APEX 22.1.0-17。
如果有人能帮助解决这个问题,我将不胜感激。提前致谢!
两个 row-level 触发器都在 accounts
table 上触发 BEFORE UPDATE
。就好像 Oracle 无法决定它并试图禁用一个触发器,该触发器只是为了通知您您正在尝试做一些您不应该做的事情并且 - 有点 - 最终陷入无限循环。
那么,哪个触发器会先 运行?据我所知,Oracle 没有指定它,而是让你 使用follows
和precedes
来决定它。像这样:
CREATE OR REPLACE TRIGGER accounts_name_surname_update_disable
BEFORE UPDATE OF client_name, client_surname
ON accounts
FOR EACH ROW
FOLLOWS accounts_insert_update --> this
BEGIN
raise_application_error (
num => -20002,
msg => 'Updating client_name and client_surname is not permitted!');
END;
可以简化此触发器(顺便说一句,您发布的代码似乎无效;没有这样的 INSERT
语句 - 应该是 SELECT
):
CREATE OR REPLACE TRIGGER accounts_insert_update
BEFORE INSERT OR UPDATE
ON accounts
FOR EACH ROW
PRECEDES accounts_name_surname_update_disable --> this
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE DISABLE';
SELECT client_name, client_surname
INTO :new.client_name, :new.client_surname
FROM clients
WHERE client_id = :new.client_id;
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE ENABLE';
END;
另一方面 - 即使可行 - 整个过程对我来说都是错误的。 accounts_name_surname_update_disable
触发器如何知道它应该 - 或不应该 - 触发并允许对这两列进行更改?对我来说,只有一个触发器就足够了——第二个(但经过修改)。
所以:放弃触发器 accounts_name_surname_update_disable
,因为它没用。
重新创建第二个触发器:不需要disable/enable任何东西,因为另一个触发器不再存在。始终从 clients
获取客户端名称(因此,有人可能 键入 这些值并不重要 - 触发器将覆盖它们):
CREATE OR REPLACE TRIGGER accounts_insert_update
BEFORE INSERT OR UPDATE
ON accounts
FOR EACH ROW
BEGIN
SELECT client_name, client_surname
INTO :new.client_name, :new.client_surname
FROM clients
WHERE client_id = :new.client_id;
END;
终于,就这些了吗?我不这么认为。整个数据模型是错误的。 ACCOUNTS
table 应该包含 only CLIENT_ID
列,这是一个外键,查看 CLIENTS.CLIENT_ID
:
create table clients
(client_id number primary key,
client_name varchar2(20) not null,
client_surname varchar2(20) not null
);
create table accounts
(account_id number primary key,
client_id number constraint fk_acc_cli references clients (client_id)
);
因此,当 accounts
需要知道客户的姓名时,查询将执行 join 并获取该数据:
select a.account_id, c.client_name
from accounts a join clients c on c.client_id = a.client_id
where a.account_id = 1234;
这应该可以解决您的问题。
我正在尝试使用预连接反规范化对这两个表进行反规范化,并且必须添加所有需要的触发器。
表格基本上是这样的:
CLIENTS
Client_ID CHAR(13),
Client_Name VARCHAR2(20),
Client_Surname VARCHAR2 (30),
...
PRIMARY KEY (Client_ID)
ACCOUNTS
Account_ID VARCHAR(20),
...
Client_ID CHAR(13),
Client_Name VARCHAR2(20),
Client_Surname VARCHAR2(30),
PRIMARY KEY (ACCOUNT_ID),
FOREIGN KEY (Client_ID) REFERENCES CLIENTS(Client_ID)
我有两个触发器与我的问题相关;一个用于禁用 Client_Name 和 Client_Surname 的手动编辑,如下所示:
CREATE OR REPLACE TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE
BEFORE UPDATE OF CLIENT_NAME, CLIENT_SURNAME ON ACCOUNTS
FOR EACH ROW
BEGIN
RAISE_APPLICATION_ERROR( NUM => -20002, MSG => 'Updating client_name and client_surname is not permitted!');
END;
第二个触发器用于在添加新帐户时自动插入客户端名称和姓氏,即连接到客户端。它看起来像这样:
CREATE OR REPLACE TRIGGER ACCOUNTS_INSERT_UPDATE
BEFORE INSERT OR UPDATE ON ACCOUNTS
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
new_name VARCHAR2(20);
new_surname VARCHAR2(30);
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE DISABLE';
INSERT Client_Name INTO new_name FROM CLIENTS
WHERE Client_ID = :NEW.Client_ID;
:NEW.Client_Name := new_name;
INSERT Client_Surname INTO new_surname FROM CLIENTS
WHERE Client_ID = :NEW.Client_ID;
:NEW.Client_Surname := new_surname;
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE ENABLE';
END;
而且有效!只要我删除 Execute immediate statements 和 pragma autonomous transaction(并手动禁用第一个触发器)。它达到了预期的效果 - 它用客户的名字和姓氏填充了行。
但是当我添加 pragma 自治事务并执行立即语句时,Apex Oracle(SQL 命令)页面冻结然后崩溃,我必须重新加载页面。
需要使用触发器来实现此效果,尽管它看起来确实不适合这项工作。
我使用的是最新版本的 Oracle Apex,在撰写此问题时为 Oracle APEX 22.1.0-17。
如果有人能帮助解决这个问题,我将不胜感激。提前致谢!
两个 row-level 触发器都在 accounts
table 上触发 BEFORE UPDATE
。就好像 Oracle 无法决定它并试图禁用一个触发器,该触发器只是为了通知您您正在尝试做一些您不应该做的事情并且 - 有点 - 最终陷入无限循环。
那么,哪个触发器会先 运行?据我所知,Oracle 没有指定它,而是让你 使用follows
和precedes
来决定它。像这样:
CREATE OR REPLACE TRIGGER accounts_name_surname_update_disable
BEFORE UPDATE OF client_name, client_surname
ON accounts
FOR EACH ROW
FOLLOWS accounts_insert_update --> this
BEGIN
raise_application_error (
num => -20002,
msg => 'Updating client_name and client_surname is not permitted!');
END;
可以简化此触发器(顺便说一句,您发布的代码似乎无效;没有这样的 INSERT
语句 - 应该是 SELECT
):
CREATE OR REPLACE TRIGGER accounts_insert_update
BEFORE INSERT OR UPDATE
ON accounts
FOR EACH ROW
PRECEDES accounts_name_surname_update_disable --> this
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE DISABLE';
SELECT client_name, client_surname
INTO :new.client_name, :new.client_surname
FROM clients
WHERE client_id = :new.client_id;
EXECUTE IMMEDIATE 'ALTER TRIGGER ACCOUNTS_NAME_SURNAME_UPDATE_DISABLE ENABLE';
END;
另一方面 - 即使可行 - 整个过程对我来说都是错误的。 accounts_name_surname_update_disable
触发器如何知道它应该 - 或不应该 - 触发并允许对这两列进行更改?对我来说,只有一个触发器就足够了——第二个(但经过修改)。
所以:放弃触发器 accounts_name_surname_update_disable
,因为它没用。
重新创建第二个触发器:不需要disable/enable任何东西,因为另一个触发器不再存在。始终从 clients
获取客户端名称(因此,有人可能 键入 这些值并不重要 - 触发器将覆盖它们):
CREATE OR REPLACE TRIGGER accounts_insert_update
BEFORE INSERT OR UPDATE
ON accounts
FOR EACH ROW
BEGIN
SELECT client_name, client_surname
INTO :new.client_name, :new.client_surname
FROM clients
WHERE client_id = :new.client_id;
END;
终于,就这些了吗?我不这么认为。整个数据模型是错误的。 ACCOUNTS
table 应该包含 only CLIENT_ID
列,这是一个外键,查看 CLIENTS.CLIENT_ID
:
create table clients
(client_id number primary key,
client_name varchar2(20) not null,
client_surname varchar2(20) not null
);
create table accounts
(account_id number primary key,
client_id number constraint fk_acc_cli references clients (client_id)
);
因此,当 accounts
需要知道客户的姓名时,查询将执行 join 并获取该数据:
select a.account_id, c.client_name
from accounts a join clients c on c.client_id = a.client_id
where a.account_id = 1234;
这应该可以解决您的问题。