5.3 响应表的特定列更新
问题
你想根据一张表中特定列的更新自动更新另外一张表中的一些特定值。举例来说,假设管理部门决定改变组织结构中的一些职位。一位新经理将出任其中的一个经理职位,那么就需要把一部分雇员信息与这位经理信息相关联。你需要想一种办法更新这些雇员的记录,让它们把对应的经理信息从前任改成现任。
解决方案
创建一个AFTER UPDATE触发器,让它在manager_id列被更新的时候执行即可。下列当中的触发器使用游标获取由前任经理所管理的雇员信息。之后会判断manager_id是否被更新,如果更新了,触发器则会循环遍历前任经理所管理的每一个雇员信息,同时把这些雇员记录的manager_id列更新为新任经理的ID。
CREATE OR REPLACE TRIGGER dept_mgr_update
AFTER UPDATE OF manager_id
ON departments
FOR EACH ROW
DECLARE
CURSOR emp_cur IS
SELECT *
FROM EMPLOYEES
WHERE manager_id = :old.manager_id
FOR UPDATE;
BEGIN
FOR emp_rec IN emp_cur LOOP
UPDATE employees
SET manager_id = :new.manager_id
WHERE CURRENT OF emp_cur;
END LOOP;
END;
/
这个触发器只有在departments表manager_id列更新时才会执行。这种类型的触发器为我们提供了良好的数据库性能,因为它并不是在departments表每次更新时都被执行。
原理分析
触发器可以只在某些特定列对应的值更新时执行。这样开发人员可以更为细致地操控触发器的执行时机。可以根据指定列的不同更新,采用不同的策略让触发器执行。就像上面的解决方案中演示的那样,可以在触发器的声明部分指定这些列。这是让触发器在指定列更新时执行的最简单的方法之一。另外一种办法就是在触发器的主体部分使用条件谓词,确定是否需要更新声明部分指定的记录。条件谓词可以和指定的列名一起使用,从而判断某些操作是否已经在指定列上执行过。可以供我们使用的条件谓词有三种,它们分别是INSERTING、UPDATING和DELETING。因此,我们可以像下面那样使用条件谓词,判断出指定列是否已经被当前语句更新过了:
IF UPDATING('my_column') THEN
-- Some statements
END IF;
使用条件谓词可以保证THEN子句中的代码,只在指定的操作在指定列上执行后才会执行。这种谓词也可以和其他的条件表达式一起使用,从而更为细致地控制执行语句。比如,你想确保某个指定列已经被更新后,并且当前日期不可以与某个结束日期相匹配,那么就可以把这两个条件用布尔操作符AND连接起来一起使用。下面的代码演示了这种条件语句的用法:
IF UPDATING('my_column') AND end_date > SYSDATE THEN
-- Some statements
END IF;
如上所述,指定一个特定的列可以减少触发器的执行次数,因为触发器只在指定列更新时才会执行。而在触发器中使用这种约束的另一个好处是,你可以按需为表增加更多的触发器。比如,需要再创建一个AFTER UPDATE触发器来更新同一个表的另外一列,使用这种技术就会减少冲突的发生。反过来说,如果是使用一个普通的AFTER UPDATE触发器,冲突的几率就会大大提升。