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