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

admin
Sun, 17 Aug 2025 12:03:04 +0000
Mysql, mysql, new, 进阶

各位听众，大家好！我是老码农，今天咱们聊聊MySQL触发器里一对好基友——NEW和OLD。这俩货，在触发器里那是相当重要，它们能让你知道数据发生了什么变化，就像数据库的秘密日记本，记录了数据的过去和未来。咱们今天就来扒一扒它们，保证你听完之后，写触发器能像写 Hello World 一样简单！ 什么是触发器？ 首先，简单回顾一下触发器。触发器（Trigger）是MySQL数据库中与表关联的特殊的存储过程，它会在特定的数据库事件发生时自动执行。这些事件可以是INSERT、UPDATE或DELETE操作。触发器就像一个默默守护数据库的小精灵，在你对数据动手动脚的时候，它会跳出来执行你预先设定的规则。 NEW和OLD：触发器中的左右护法 现在，重点来了，NEW和OLD这两个关键字，是触发器中最常用的变量，它们分别代表： NEW： 用于访问即将插入的新行或即将更新的行的新值。 OLD： 用于访问即将删除的行或即将更新的行的旧值。 你可以把 NEW 看作是“新来的”，OLD 看作是“旧的”。它们就像两个时光机，一个带你去看数据的未来，一个带你回忆数据的过去。 NEW和OLD的使用场景 INSER …

继续阅读“MySQL编程进阶之：`NEW`和`OLD`关键字：在触发器中获取新旧数据的实践。”

The post MySQL编程进阶之：`NEW`和`OLD`关键字：在触发器中获取新旧数据的实践。 first appeared on 智猿学院-前后端，数据库，人工智能，云计算等领域前沿技术讲座.

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

什么是触发器？

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

NEW和OLD：触发器中的左右护法

现在，重点来了，NEW和OLD这两个关键字，是触发器中最常用的变量，它们分别代表：

NEW： 用于访问即将插入的新行或即将更新的行的新值。

OLD： 用于访问即将删除的行或即将更新的行的旧值。

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

NEW和OLD的使用场景

INSERT触发器： 只能使用 NEW，因为插入操作没有“旧”的数据。

UPDATE触发器： 可以同时使用 NEW 和 OLD，因为更新操作既有旧值也有新值。

DELETE触发器： 只能使用 OLD，因为删除操作之后就没有“新”的数据了。

实战演练：NEW和OLD的用法示例

光说不练假把式，咱们直接上代码！

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.id、NEW.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.id、OLD.name等来访问被删除员工的信息。因为是DELETE触发器，所以只能用OLD。

测试一下：

DELETE FROM employees WHERE name = '张三';

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

NEW和OLD的只读性

需要注意的是，在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触发器中，NEW和OLD的值都是只读的，不能被修改。这是因为AFTER触发器是在数据已经插入或更新之后执行的，修改NEW或OLD的值已经没有意义了。

NEW和OLD为NULL的情况

INSERT触发器中的OLD： OLD总是NULL，因为插入操作没有旧数据。

DELETE触发器中的NEW： NEW总是NULL，因为删除操作没有新数据。

UPDATE触发器中，如果某个字段没有被更新： NEW.column_name和OLD.column_name的值可能相同，但它们仍然是不同的变量，分别代表旧值和新值。如果确实要检测字段是否被更新，可以使用IF OLD.column_name <> NEW.column_name THEN ... END IF;。

总结：NEW和OLD的表格对比

为了方便大家记忆，我们用一个表格来总结一下NEW和OLD的特性：

特性 | NEW | OLD
适用触发器 | INSERT、UPDATE | UPDATE、DELETE
代表 | 即将插入的新行/即将更新的行的新值 | 即将删除的行/即将更新的行的旧值
可修改性 | BEFORE触发器中可修改，AFTER触发器中只读 | AFTER触发器中只读
为NULL的情况 | DELETE触发器中总是NULL | INSERT触发器中总是NULL

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

避免循环触发： 触发器内部的操作可能会再次触发其他触发器，导致无限循环。要小心设计触发器，避免这种情况发生。

控制触发器的复杂度： 触发器不应该执行过于复杂的操作，否则会影响数据库的性能。

仔细测试： 在生产环境中使用触发器之前，一定要进行充分的测试，确保触发器的行为符合预期。

适当使用： 触发器是把双刃剑，用的好能极大提高数据一致性和业务流程的自动化，但是滥用会导致性能问题，可维护性变差等问题，要充分考虑使用场景和替代方案，不宜过度依赖触发器。

举一反三：更多NEW和OLD的应用场景

数据校验： 在BEFORE INSERT或BEFORE UPDATE触发器中，可以使用NEW来校验数据的有效性，例如，检查邮箱格式是否正确，年龄是否在合理范围内。

自动生成数据： 在BEFORE INSERT触发器中，可以使用NEW来自动生成一些数据，例如，根据姓名自动生成拼音首字母，或者根据生日自动计算年龄。

记录操作日志： 可以使用NEW和OLD来记录数据的变更历史，例如，记录哪个用户在什么时间修改了哪些数据。

实现复杂的业务逻辑： 可以使用触发器来实现一些复杂的业务逻辑，例如，当订单状态变为“已发货”时，自动更新库存数量。

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