本章讲解如何用 Oracle APEX 的 Automation(自动化)定义重复性处理。自动化属于 Shared Components,可以按计划运行,也可以由开发者按需触发;它适合定期检查数据、逐行执行逻辑、发送通知、调用远程处理、记录执行日志,以及把后台规则从页面提交流程中拆出来。
19 执行重复性处理#
在 APEX 应用中,许多业务规则不应依赖用户打开页面后才执行。例如每周挑选一名员工、定期同步状态、批量发送提醒、检查即将过期的数据,或在后台补充地理编码信息。Automation 提供了一个声明式入口:先定义它处理哪些行,再定义每一行或整次运行要执行哪些动作,最后决定它是启用计划,还是只允许手工或 API 调用。
学习前提#
- 能进入目标应用的 App Builder 和 Shared Components。
- 已有可实验的数据表,例如 Woods HR 示例中的
EBA_DEMO_EMP。 - 理解基本 SQL、PL/SQL、应用项(Application Item)和服务器端条件(Server-side Condition)。
- 如果要验证邮件动作,需要工作区邮件配置可用,或至少能查看邮件队列与自动化日志。
来源:Oracle APEX 26.1 官方文档:Executing Repetitive Processing
19.1 理解自动化核心概念#
Automation 是一个可重复运行的后台作业。它既可以按 Schedule Expression 定期执行,也可以从 Automations 列表页、PL/SQL API 或当前用户会话中按需执行。理解自动化时,先把它拆成四层:数据来源、动作序列、调度与状态、日志与错误处理。
- 动作顺序:自动化包含一个或多个 Actions。动作可以执行本地或远程数据库代码,也可以发送邮件、推送通知;在 Autonomous AI Database 上还可以使用服务器端地理编码。
- 运行粒度:动作序列可以只运行一次,也可以按 Source Query 返回的每一行重复运行。逐行处理时,查询列名可作为绑定变量引用当前行值。
- 条件控制:自动化本身和每个动作都能配置 Server-side condition,用于只在特定场景下执行。
- 调度表达式:常见周期可用 Interval Builder 生成;复杂周期可直接输入数据库调度器支持的 Calendar Expression。
- 启停状态:Schedule Status 为启用时,APEX 引擎按计划运行。禁用时,它不会按计划运行,但仍可通过列表页或
APEX_AUTOMATION.EXECUTE手工执行。 - 错误处理:默认情况下,一个动作出错后,自动化会继续执行后续动作和剩余行。可改为遇错终止,并选择保留下一次计划或禁用自动化。
- 日志:可在 App Builder 查看自动化日志,也可查询
APEX_AUTOMATION_LOG。动作代码可调用APEX_AUTOMATION.LOG_INFO、LOG_WARN、LOG_ERROR写入自定义信息。 - 程序控制:
APEX_AUTOMATION支持EXECUTE、RESCHEDULE、ENABLE、DISABLE、TERMINATE。动作代码还可以调用SKIP_CURRENT_ROW跳过当前行,或调用EXIT提前结束自动化。
自动化还可以配置辅助逻辑,包括 Initialization Procedure、Cleanup Procedure 和 Before Row Processing Procedure。这些过程可以引用或设置应用项,把它们当作本次运行中的临时状态。需要注意的是,如果动作修改了当前行绑定变量的值,APEX 不会自动把这些改动保存回数据库;真正的持久化必须由动作代码执行 UPDATE、调用 API 或提交业务过程。
操作与验收#
- 位置:Shared Components -> Automations。
- 动作:打开或新建一个自动化,检查 Source、Actions、Schedule、Additional Code Execution、Error Handling 和 Execution Log 相关配置。
- 检查点:能指出哪些配置决定“处理哪些行”、哪些配置决定“做什么”、哪些配置决定“何时运行”。
- 预期结果:能把一个定时业务规则拆解成 Source Query、动作序列、调度表达式和日志策略。
来源:Oracle APEX 26.1 官方文档:Understanding Key Automation Concepts
19.2 探索自动化示例#
官方示例使用 Woods HR 应用中的 Saturday Employee Lottery 自动化:每周六随机挑选一名低收入员工,为其加薪 3%;如果获奖者是销售人员并已有佣金,也给佣金增加 3%。这个例子展示了自动化的完整闭环:用查询找候选人,用初始化代码生成随机中奖序号,用动作序列跳过未中奖行、更新薪资和佣金、发送通知,最后通过日志确认执行结果。
- 19.2.1 每周六赢得加薪
- 19.2.2 识别自动化要处理的行
- 19.2.3 配置自动化辅助逻辑
- 19.2.4 按顺序定义自动化动作
- 19.2.5 建立自动化调度
- 19.2.6 按需运行自动化
- 19.2.7 查看自动化日志
来源:Oracle APEX 26.1 官方文档:Exploring an Automation Example
19.2.1 每周六赢得加薪#
Woods Clinic 管理层希望用一个轻量的激励机制奖励收入较低的员工:每周六从候选员工中随机选出一人,把薪资提高 3%。如果中奖者是销售人员,且已有佣金,则佣金也提高 3%。示例中,员工 MARTIN 收到来自 hr@example.com 的邮件,主题是 You Won the Employee Salary Lottery!,邮件正文告知新的薪资和佣金。
操作与验收#
- 位置:业务运行页面、邮件模板和自动化日志。
- 动作:确认示例业务结果:员工薪资被更新,销售人员佣金按条件更新,中奖员工收到邮件。
- 检查点:邮件中的员工姓名、新薪资、新佣金与数据库更新结果一致。
- 预期结果:能把“每周抽奖加薪”转化为一个可验证的后台处理目标,而不是只看作一次页面交互。
来源:Oracle APEX 26.1 官方文档:Winning Salary Increase Every Saturday
19.2.2 识别自动化要处理的行#
自动化的 Source Query 决定本次运行要处理哪些行。本例只从低收入员工中抽奖,因此查询先用 PERCENTILE_CONT 计算薪资最低 25% 的分位线,再筛选出薪资小于等于该界限的员工。查询还把 ROWNUM 起别名为 RN,用于把“随机中奖序号”映射到候选员工行。
with cutoff as (
select percentile_cont(0.25)
within group (order by sal) as salary_limit
from eba_demo_emp
)
select e.empno,
e.ename,
e.job,
e.sal,
e.comm,
rownum as rn,
nvl(:G_DEMO_EMAIL,'demo.user@example.com') as employee_email,
null as new_salary,
null as new_commission
from eba_demo_emp e
join cutoff c
on e.sal <= c.salary_limit
NEW_SALARY 和 NEW_COMMISSION 是额外的临时列,初始为 NULL。后续动作可以给它们赋值,再由更靠后的动作读取。它们只在当前行处理期间存在于内存里,类型始终按 VARCHAR2 处理;如果要保存,动作代码必须显式更新表。
操作与验收#
- 位置:Shared Components -> Automations -> 自动化编辑页 -> Source 标签页。
- 动作:粘贴或复现候选员工 Source Query,确认查询返回低收入候选人、
RN、邮件地址和两个临时列。 - 检查点:查询结果中的每行都有唯一序号,且候选人范围与最低 25% 薪资规则一致。
- 预期结果:后续动作可以通过
:EMPNO、:ENAME、:RN、:NEW_SALARY等绑定变量访问当前行上下文。
来源:Oracle APEX 26.1 官方文档:Identifying Automation Rows to Process
19.2.3 配置自动化辅助逻辑#
自动化可以在动作运行前执行初始化过程。本例在 Additional Code Execution 中内联定义 INIT_EMP_LOTTERY,用与 Source Query 相同的低收入规则统计候选人数,再生成 1 到候选人数之间的随机整数,保存到应用项 G_LOTTERY_PICK。动作序列随后用 :RN = :G_LOTTERY_PICK 判断当前员工是否中奖。
procedure init_emp_lottery is
l_cnt number;
begin
dbms_random.seed(dbms_utility.get_hash_value(
rawtohex(sys_guid()), 0, 2147483647));
select count(*)
into l_cnt
from eba_demo_emp e
where e.sal <= (select percentile_cont(0.25)
within group (order by sal)
from eba_demo_emp);
:G_LOTTERY_PICK := trunc(dbms_random.value(1, l_cnt + 1));
apex_automation.log_info(
apex_string.format('Picked lucky number for today is %s',
:G_LOTTERY_PICK));
end init_emp_lottery;
因为该过程是自动化内联代码,可以直接用绑定变量读写应用项。如果初始化过程是数据库中已有的外部过程,则需要通过 APEX_SESSION_STATE.SET_VALUE 设置应用项,并用 V('ITEM_NAME') 读取值。
init_emp_lottery 初始化过程。操作与验收#
- 位置:自动化编辑页 -> Additional Code Execution。
- 动作:在 Executable PL/SQL Code 中定义初始化过程,并把 Initialization Procedure 设置为
init_emp_lottery。 - 检查点:运行自动化后,日志中出现被抽中的随机序号。
- 预期结果:
G_LOTTERY_PICK在本次自动化运行中可被后续逐行动作引用。
来源:Oracle APEX 26.1 官方文档:Configuring Supporting Automation Logic
19.2.4 按顺序定义自动化动作#
Saturday Employee Lottery 的动作序列包含五个动作。它们按顺序执行,前面的动作可以决定是否跳过当前行,也可以把计算结果放入当前行的临时列,供后续动作使用。
- Log Employee Name Being Considered
- Give 3% Raise to Lucky Employee
- Bump Lucky Salesperson's Commission, Too
- Update Commission If Changed
- Email Lucky Recipient the Good News
19.2.4.1 记录正在检查的员工#
第一个动作是 Execute Code。它比较当前行的 :RN 与初始化过程生成的 :G_LOTTERY_PICK。如果当前行中奖,就写入日志并继续后续动作;如果没有中奖,就调用 APEX_AUTOMATION.SKIP_CURRENT_ROW,直接进入下一行,避免对未中奖员工执行加薪、佣金更新和邮件发送。
if :RN = :G_LOTTERY_PICK then
apex_automation.log_info(
apex_string.format('Processing bottom quartile lucky employee %s with rn=%s',
:ENAME, :RN));
else
apex_automation.skip_current_row(
apex_string.format('Skipping bottom quartile employee %s with rn=%s',
:ENAME, :RN));
end if;
19.2.4.2 给中奖员工加薪 3%#
第二个动作也使用 Execute Code,并配置服务器端条件 :RN = :G_LOTTERY_PICK,确保只对中奖员工执行。动作先计算新薪资并赋值给临时列 :NEW_SALARY,再更新 EBA_DEMO_EMP.SAL,最后写入日志。这个临时值稍后还会传给邮件模板占位符。
:RN = :G_LOTTERY_PICK
:NEW_SALARY := round(to_number(:SAL) * 1.03);
apex_automation.log_info(
apex_string.format('Lucky employee %s gets 3% salary raise from %s to %s',
:ENAME, :SAL, :NEW_SALARY));
update eba_demo_emp
set sal = :NEW_SALARY
where empno = :EMPNO;
apex_automation.log_info(
apex_string.format('Updated Lucky employee %s salary from %s to %s',
:ENAME, :SAL, :NEW_SALARY));
19.2.4.3 如果中奖者是销售人员,也提高佣金#
第三个动作只针对中奖、岗位为 SALESMAN 且原佣金不为空的员工。它计算 3% 的新佣金,把值放入 :NEW_COMMISSION,但不在本动作中更新数据库;真正保存由下一动作完成。
:RN = :G_LOTTERY_PICK and :JOB = 'SALESMAN' and :COMM is not null
:NEW_COMMISSION := round(to_number(:COMM) * 1.03);
apex_automation.log_info(
apex_string.format('Lucky Salesperson %s also gets 3% commission bump from %s to %s',
:ENAME, :COMM, :NEW_COMMISSION));
19.2.4.4 在佣金发生变化时更新数据库#
第四个动作的服务器端条件是 :NEW_COMMISSION is not null。如果前一动作设置了新佣金,它就把 EBA_DEMO_EMP.COMM 更新为该值,并写入日志。这展示了“前一动作准备临时值,后一动作持久化”的常见模式。
:NEW_COMMISSION is not null
update eba_demo_emp
set comm = :NEW_COMMISSION
where empno = :EMPNO;
apex_automation.log_info(
apex_string.format('Updated Lucky Salesperson %s commission from %s to %s',
:ENAME, :COMM, :NEW_COMMISSION));
19.2.4.5 给中奖者发送邮件#
最后一个动作是 Send E-Mail,服务器端条件同样为 :RN = :G_LOTTERY_PICK。它使用 Email Salary Lottery Winner 邮件模板,模板占位符从当前行列名取值,例如员工姓名、邮箱、新薪资和可能的新佣金。
操作与验收#
- 位置:自动化编辑页 -> Actions 标签页。
- 动作:按上述顺序创建五个动作,分别配置动作类型、PL/SQL 代码、服务器端条件和邮件模板。
- 检查点:未中奖员工只产生跳过日志;中奖员工产生薪资更新日志;销售人员中奖时还产生佣金更新日志。
- 预期结果:一次自动化运行只处理一名中奖员工,且邮件内容与持久化后的薪资和佣金一致。
来源:Oracle APEX 26.1 官方文档:Defining Automation Actions in Sequence
19.2.5 建立自动化调度#
自动化的 Schedule Expression 决定它何时运行。对于每小时、每天、每周、每月这类常见周期,优先使用 Interval Builder,它能减少语法错误。对于“每月最后一个星期六”这类复杂规则,可以直接输入数据库调度器支持的表达式。
操作与验收#
- 位置:自动化编辑页的调度相关属性,尤其是 Schedule Expression 和 Schedule Status。
- 动作:先用 Interval Builder 生成常见周期,再手工检查表达式含义。
- 检查点:Next Run 与业务预期时间一致,并且时区符合应用部署环境。
- 预期结果:自动化启用后按业务周期运行;禁用时只在开发者手工或 API 调用时运行。
来源:Oracle APEX 26.1 官方文档:Establishing an Automation's Schedule
19.2.5.1 使用 Interval Builder 调度作业#
Interval Builder 用表单方式生成紧凑的调度表达式。示例选择 Frequency = Weekly、Interval = 1、Execution Day = Sat、Execution Hour = 20、Execution Minute = 50,得到的含义是每周六 20:50 运行。
FREQ=WEEKLY;INTERVAL=1;BYDAY=SAT;BYHOUR=20;BYMINUTE=50
操作与验收#
- 位置:自动化编辑页中 Schedule Expression 旁的扳手与时钟按钮。
- 动作:用对话框选择每周、间隔 1、星期六、20:50。
- 检查点:生成的表达式包含
FREQ=WEEKLY、BYDAY=SAT、BYHOUR=20、BYMINUTE=50。 - 预期结果:表达式能被 APEX 接受,并在自动化列表中计算出下一次运行时间。
来源:Oracle APEX 26.1 官方文档:Using Interval Builder to Schedule Job
19.2.5.2 使用更复杂的调度表达式#
Schedule Expression 支持 DBMS_SCHEDULER 可接受的 Calendar Expression。表达式由分号分隔的 KEYWORD=VALUE 片段组成。常用关键字包括必填的 FREQ、用于表示重复间隔的 INTERVAL,以及用于精确指定日期时间元素、集合操作和周期限制的过滤条件。
复杂表达式上线前应先验证未来运行时间。可以在 SQL Commands 中运行下面的 PL/SQL 块,让数据库调度器计算接下来的五次触发时间。
declare
procedure next_five_dates(
p_calendar_string in varchar2)
is
l_next_date timestamp with time zone;
l_return_date_after timestamp with time zone := systimestamp;
l_start_date timestamp with time zone := systimestamp;
begin
dbms_output.put_line(p_calendar_string);
for i in 1..5 loop
dbms_scheduler.evaluate_calendar_string(
calendar_string => p_calendar_string,
start_date => l_start_date,
return_date_after => l_return_date_after,
next_run_date => l_next_date);
dbms_output.put_line('Next ' || i || ': ' ||
to_char(l_next_date,
'DY DD-MON-YYYY HH24:MI:SS TZR'));
l_return_date_after := l_next_date;
end loop;
end;
begin
-- Every week on Saturday at 20:50
next_five_dates('FREQ=WEEKLY;INTERVAL=1;BYDAY=SAT;BYHOUR=20;BYMINUTE=50');
-- Last Saturday of the month at 20:50
next_five_dates('FREQ=MONTHLY;INTERVAL=1;BYDAY=-1 SAT;BYHOUR=20;BYMINUTE=50');
end;
示例输出会分别列出“每周六 20:50”和“每月最后一个星期六 20:50”的后续触发时间。检查这类输出时,要特别关注时区、夏令时、月底边界以及业务假日策略。
FREQ=WEEKLY;INTERVAL=1;BYDAY=SAT;BYHOUR=20;BYMINUTE=50
Next 1: SAT 04-OCT-2025 20:50:16 +00:00
Next 2: SAT 11-OCT-2025 20:50:16 +00:00
Next 3: SAT 18-OCT-2025 20:50:16 +00:00
Next 4: SAT 25-OCT-2025 20:50:16 +00:00
Next 5: SAT 01-NOV-2025 20:50:16 +00:00
FREQ=MONTHLY;INTERVAL=1;BYDAY=-1 SAT;BYHOUR=20;BYMINUTE=50
Next 1: SAT 25-OCT-2025 20:50:16 +00:00
Next 2: SAT 29-NOV-2025 20:50:16 +00:00
Next 3: SAT 27-DEC-2025 20:50:16 +00:00
Next 4: SAT 31-JAN-2026 20:50:16 +00:00
Next 5: SAT 28-FEB-2026 20:50:16 +00:00
操作与验收#
- 位置:SQL Workshop -> SQL Commands,以及自动化的 Schedule Expression 属性。
- 动作:用
DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING测试候选表达式。 - 检查点:输出的未来运行时间与业务规则一致,例如“每月最后一个星期六”不会被误写成“每周六”。
- 预期结果:只有通过未来日期验证的表达式才写回自动化配置。
来源:Oracle APEX 26.1 官方文档:Using More Complex Schedule Expressions
19.2.6 按需运行自动化#
开发、测试和补跑时,可以从 Automations 列表页手工运行自动化。列表的 Run 列提供“播放”按钮。若 Schedule Status 为 Disabled,点击后只运行一次;若状态为 Active,点击后会立即运行,并继续根据配置的 Schedule Expression 计算下一次运行时间。
操作与验收#
- 位置:Shared Components -> Automations 列表页。
- 动作:点击 Saturday Employee Lottery 行的 Run 播放按钮。
- 检查点:列表或日志中出现新的运行记录;启用计划时,Next Run 也随之更新。
- 预期结果:可以在不等待真实周六计划触发的情况下验证自动化逻辑。
来源:Oracle APEX 26.1 官方文档:Running an Automation on Demand
19.2.7 查看自动化日志#
自动化运行后,应先查看 Execution Log。该标签页列出近期运行、处理行数和消息数量。点击 Messages 列中的数量,可以进入某次运行的消息页面,查看动作代码写入的日志、排序和下载交互式报表结果。
操作与验收#
- 位置:Shared Components -> Automations -> Execution Log。
- 动作:打开最新运行记录,点击 Messages 数量查看消息明细。
- 检查点:能看到初始化过程写入的中奖序号、未中奖员工跳过信息、中奖员工薪资和佣金更新信息。
- 预期结果:日志能解释本次自动化为什么处理了某一行、跳过了其他行,以及最终是否成功完成。