各位听众,大家好!我是老码农,今天咱们聊聊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
,你就能像一个魔法师一样,轻松驾驭你的数据库!有问题随时来找老码农,咱们下次再见!