MySQL编程进阶之:`NEW`和`OLD`关键字:在触发器中获取新旧数据的实践。

各位听众,大家好!我是老码农,今天咱们聊聊MySQL触发器里一对好基友——NEWOLD。这俩货,在触发器里那是相当重要,它们能让你知道数据发生了什么变化,就像数据库的秘密日记本,记录了数据的过去和未来。咱们今天就来扒一扒它们,保证你听完之后,写触发器能像写 Hello World 一样简单!

什么是触发器?

首先,简单回顾一下触发器。触发器(Trigger)是MySQL数据库中与表关联的特殊的存储过程,它会在特定的数据库事件发生时自动执行。这些事件可以是INSERTUPDATEDELETE操作。触发器就像一个默默守护数据库的小精灵,在你对数据动手动脚的时候,它会跳出来执行你预先设定的规则。

NEWOLD:触发器中的左右护法

现在,重点来了,NEWOLD这两个关键字,是触发器中最常用的变量,它们分别代表:

  • NEW 用于访问即将插入的新行或即将更新的行的值。
  • OLD 用于访问即将删除的行或即将更新的行的值。

你可以把 NEW 看作是“新来的”,OLD 看作是“旧的”。它们就像两个时光机,一个带你去看数据的未来,一个带你回忆数据的过去。

NEWOLD的使用场景

  • INSERT触发器: 只能使用 NEW,因为插入操作没有“旧”的数据。
  • UPDATE触发器: 可以同时使用 NEWOLD,因为更新操作既有旧值也有新值。
  • DELETE触发器: 只能使用 OLD,因为删除操作之后就没有“新”的数据了。

实战演练:NEWOLD的用法示例

光说不练假把式,咱们直接上代码!

1. INSERT触发器:记录新增员工信息

假设我们有一个employees表,记录员工信息:

CREATE TABLE employees (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL,
    salary DECIMAL(10, 2) NOT NULL,
    hire_date DATE NOT NULL
);

现在,我们想在每次新增员工时,把新增的员工信息记录到另一个表employee_audit中:

CREATE TABLE employee_audit (
    id INT PRIMARY KEY AUTO_INCREMENT,
    employee_id INT NOT NULL,
    name VARCHAR(255) NOT NULL,
    salary DECIMAL(10, 2) NOT NULL,
    hire_date DATE NOT NULL,
    action VARCHAR(10) NOT NULL,
    action_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

创建触发器:

DELIMITER //

CREATE TRIGGER before_employee_insert
BEFORE INSERT
ON employees
FOR EACH ROW
BEGIN
    INSERT INTO employee_audit (employee_id, name, salary, hire_date, action)
    VALUES (NEW.id, NEW.name, NEW.salary, NEW.hire_date, 'INSERT');
END//

DELIMITER ;

代码解释:

  • DELIMITER //: 修改语句结束符为 //,因为触发器内部包含多个SQL语句。
  • CREATE TRIGGER before_employee_insert: 创建一个名为before_employee_insert的触发器。
  • BEFORE INSERT: 指定触发器在INSERT操作之前执行。
  • ON employees: 指定触发器与employees表关联。
  • FOR EACH ROW: 指定触发器对每一行数据都执行。
  • BEGIN ... END: 触发器的代码块。
  • INSERT INTO employee_audit ... VALUES (NEW.id, NEW.name, ...): 将新增员工的信息插入到employee_audit表中。注意这里使用了NEW.idNEW.name等来访问新增员工的信息。因为是INSERT触发器,所以只能用NEW

测试一下:

INSERT INTO employees (name, salary, hire_date) VALUES ('张三', 5000.00, '2023-01-01');

现在,查看employee_audit表,你应该能看到新插入的员工信息。

2. UPDATE触发器:记录员工工资变化

我们想在每次更新员工工资时,记录员工的旧工资和新工资:

CREATE TABLE salary_audit (
    id INT PRIMARY KEY AUTO_INCREMENT,
    employee_id INT NOT NULL,
    old_salary DECIMAL(10, 2) NOT NULL,
    new_salary DECIMAL(10, 2) NOT NULL,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

创建触发器:

DELIMITER //

CREATE TRIGGER after_employee_update
AFTER UPDATE
ON employees
FOR EACH ROW
BEGIN
    IF OLD.salary <> NEW.salary THEN
        INSERT INTO salary_audit (employee_id, old_salary, new_salary)
        VALUES (NEW.id, OLD.salary, NEW.salary);
    END IF;
END//

DELIMITER ;

代码解释:

  • AFTER UPDATE: 指定触发器在UPDATE操作之后执行。
  • IF OLD.salary <> NEW.salary THEN ... END IF;: 判断员工的旧工资和新工资是否相同,只有在工资发生变化时才执行插入操作。
  • VALUES (NEW.id, OLD.salary, NEW.salary): 将员工ID、旧工资和新工资插入到salary_audit表中。这里同时使用了NEW.id(员工ID) OLD.salary(旧工资)和NEW.salary(新工资)。

测试一下:

UPDATE employees SET salary = 6000.00 WHERE name = '张三';

现在,查看salary_audit表,你应该能看到张三的旧工资和新工资的记录。

3. DELETE触发器:记录被删除的员工信息

我们想在每次删除员工时,把被删除的员工信息记录到employee_audit表中:

DELIMITER //

CREATE TRIGGER before_employee_delete
BEFORE DELETE
ON employees
FOR EACH ROW
BEGIN
    INSERT INTO employee_audit (employee_id, name, salary, hire_date, action)
    VALUES (OLD.id, OLD.name, OLD.salary, OLD.hire_date, 'DELETE');
END//

DELIMITER ;

代码解释:

  • BEFORE DELETE: 指定触发器在DELETE操作之前执行。
  • VALUES (OLD.id, OLD.name, ...): 将被删除员工的信息插入到employee_audit表中。注意这里使用了OLD.idOLD.name等来访问被删除员工的信息。因为是DELETE触发器,所以只能用OLD

测试一下:

DELETE FROM employees WHERE name = '张三';

现在,查看employee_audit表,你应该能看到被删除的员工信息。

NEWOLD的只读性

需要注意的是,在BEFORE触发器中,我们可以修改NEW的值,从而影响实际插入或更新的数据。例如,我们可以在插入员工之前,自动将员工姓名转换为大写:

DELIMITER //

CREATE TRIGGER before_employee_insert_uppercase
BEFORE INSERT
ON employees
FOR EACH ROW
BEGIN
    SET NEW.name = UPPER(NEW.name);
END//

DELIMITER ;

但是,在AFTER触发器中,NEWOLD的值都是只读的,不能被修改。这是因为AFTER触发器是在数据已经插入或更新之后执行的,修改NEWOLD的值已经没有意义了。

NEWOLD为NULL的情况

  • INSERT触发器中的OLD OLD总是NULL,因为插入操作没有旧数据。
  • DELETE触发器中的NEW NEW总是NULL,因为删除操作没有新数据。
  • UPDATE触发器中,如果某个字段没有被更新: NEW.column_nameOLD.column_name的值可能相同,但它们仍然是不同的变量,分别代表旧值和新值。如果确实要检测字段是否被更新,可以使用IF OLD.column_name <> NEW.column_name THEN ... END IF;

总结:NEWOLD的表格对比

为了方便大家记忆,我们用一个表格来总结一下NEWOLD的特性:

特性 NEW OLD
适用触发器 INSERTUPDATE UPDATEDELETE
代表 即将插入的新行/即将更新的行的 即将删除的行/即将更新的行的
可修改性 BEFORE触发器中可修改,AFTER触发器中只读 AFTER触发器中只读
NULL的情况 DELETE触发器中总是NULL INSERT触发器中总是NULL

最佳实践:触发器的注意事项

  • 避免循环触发: 触发器内部的操作可能会再次触发其他触发器,导致无限循环。要小心设计触发器,避免这种情况发生。
  • 控制触发器的复杂度: 触发器不应该执行过于复杂的操作,否则会影响数据库的性能。
  • 仔细测试: 在生产环境中使用触发器之前,一定要进行充分的测试,确保触发器的行为符合预期。
  • 适当使用: 触发器是把双刃剑,用的好能极大提高数据一致性和业务流程的自动化,但是滥用会导致性能问题,可维护性变差等问题,要充分考虑使用场景和替代方案,不宜过度依赖触发器。

举一反三:更多NEWOLD的应用场景

  • 数据校验:BEFORE INSERTBEFORE UPDATE触发器中,可以使用NEW来校验数据的有效性,例如,检查邮箱格式是否正确,年龄是否在合理范围内。
  • 自动生成数据:BEFORE INSERT触发器中,可以使用NEW来自动生成一些数据,例如,根据姓名自动生成拼音首字母,或者根据生日自动计算年龄。
  • 记录操作日志: 可以使用NEWOLD来记录数据的变更历史,例如,记录哪个用户在什么时间修改了哪些数据。
  • 实现复杂的业务逻辑: 可以使用触发器来实现一些复杂的业务逻辑,例如,当订单状态变为“已发货”时,自动更新库存数量。

好了,今天的讲座就到这里。希望通过今天的讲解,大家对MySQL触发器中的NEWOLD有了更深入的了解。记住,熟练掌握NEWOLD,你就能像一个魔法师一样,轻松驾驭你的数据库!有问题随时来找老码农,咱们下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注