存储过程:
USE my_suoyin_learning; DELIMITER // CREATE PROCEDURE p11(IN uage INT) BEGIN -- 第1层:普通变量 DECLARE uname VARCHAR(100); DECLARE upro VARCHAR(100); DECLARE done INT DEFAULT 0; -- 第2层:游标(必须放在handler之前) DECLARE u_cursor CURSOR FOR SELECT username,job FROM user_info WHERE age<=uage; -- 第3层:NOT FOUND异常处理器(最后写) DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; -- 创建目标表 CREATE TABLE IF NOT EXISTS tb_user_pro( id INT PRIMARY KEY AUTO_INCREMENT , NAME VARCHAR(100), profession VARCHAR(100) ); OPEN u_cursor; WHILE done = 0 DO FETCH u_cursor INTO uname,upro; IF done = 0 THEN INSERT INTO tb_user_pro VALUES(NULL, uname, upro); END IF; END WHILE; CLOSE u_cursor; END // DELIMITER ; CALL p11(23); SELECT * FROM tb_user_pro;一次性修正全套代码 1、先删掉旧触发器 sql DROP TRIGGER IF EXISTS tb_user_insert_trigger; 2、重新创建日志表(统一字段名) sql CREATE TABLE IF NOT EXISTS user_logs ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '日志编号', uid INT COMMENT '新增用户ID', operate_time DATETIME COMMENT '操作时间', action VARCHAR(50) COMMENT '操作类型' ); 3、重新创建触发器(字段严格匹配) sql DELIMITER // CREATE TRIGGER tb_user_insert_trigger AFTER INSERT ON user_info FOR EACH ROW BEGIN INSERT INTO user_logs(uid, operate_time, action) VALUES(NEW.id, NOW(), '新增用户'); END // DELIMITER ; 4、再次执行插入测试 sql INSERT INTO user_info(id,username,phone) VALUES(10,'张三','123456'); 5、查看日志是否写入成功 sql SELECT * FROM user_logs; 补充:如果你就想用 operation 这个字段名 把建表和触发器统一改成 operation: sql DROP TABLE IF EXISTS user_logs; CREATE TABLE user_logs ( id INT PRIMARY KEY AUTO_INCREMENT, uid INT, operate_time DATETIME, operation VARCHAR(50) ); DELIMITER // CREATE TRIGGER tb_user_insert_trigger AFTER INSERT ON user_info FOR EACH ROW BEGIN INSERT INTO user_logs(uid, operate_time, operation) VALUES(NEW.id, NOW(), '新增用户'); END // DELIMITER ; 核心规则:INSERT 括号里的字段名,必须和数据表真实字段完全一模一样,一字不差。触发器总结:
两段触发器代码完整解析
整体说明
你现在写了两个触发器:
新增触发器:给
tb_user插入数据之后,自动往日志表user_logs记录插入详情删除触发器:给
tb_user删除数据之后,自动往日志表user_logs记录删除前的数据 用到两个关键字:
NEW:INSERT/UPDATE 触发时,代表新增 / 修改后的新数据行OLD:DELETE/UPDATE 触发时,代表删除 / 修改前的旧数据行
第一段:插入触发器 tb_user_insert_trigger
sql
create trigger tb_user_insert_trigger after insert on tb_user for each row begin insert into user_logs(id, operation, operate_time, operate_id, operate_params) VALUES (null, 'insert', now(), new.id, concat('插入的数据内容为: id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=',new.email)); end;逐行拆解
create trigger tb_user_insert_trigger创建触发器,名字:tb_user_insert_triggerafter insert on tb_user for each row
after insert:插入 tb_user 数据完成之后再执行触发器for each row:每插入一行数据,就触发一次
begin ... end:触发器要执行的 SQL 逻辑体内部插入日志逻辑:
sql
insert into user_logs(字段) values(值)表格
字段 | 填入内容 | 含义 |
|---|---|---|
id | null | 日志主键自增,填 null 自动生成 |
operation | 'insert' | 操作类型:新增 |
operate_time | now() | 当前系统时间 |
operate_id | new.id | 本次新增用户的 id(NEW = 刚插入的新行数据) |
operate_params | concat 拼接字符串 | 把本次插入的所有字段打包成文本存起来 |
触发场景
执行插入语句:
sql
INSERT INTO tb_user(id,name,phone,email) VALUES(1,'张三','13800138000','zs@qq.com');tb_user 插入成功瞬间,自动在user_logs生成一条插入日志。
第二段:删除触发器 tb_user_delete_trigger
sql
create trigger tb_user_delete_trigger after delete on tb_user for each row begin insert into user_logs(id, operation, operate_time, operate_id, operate_params) VALUES (null, 'delete', now(), old.id, concat('删除之前的数据: id=',old.id,',name=',old.name, ', phone=', old.phone, ', email=', old.email, ', profession=',old.profession)); end;重点区别
after delete on tb_user:删除 tb_user 数据之后触发必须用 OLD 关键字删除操作数据已经没了,只能用
OLD获取删除前原本存在的整条数据operation = 'delete':标记本次操作为删除
concat 把删除前所有字段拼接保存,方便后期回溯找回删掉的数据
触发场景
执行删除语句:
sql
DELETE FROM tb_user WHERE id=1;数据删掉之后,自动写入一条删除日志,留存删除前完整信息。
配套查看命令
1. 查看当前库所有触发器(你执行的 show triggers;)
sql
show triggers;运行后表格会列出:触发器名称、触发时机、绑定表、触发语句等信息,能验证两个触发器都创建成功。
2. 查看日志记录(验证触发器有没有生效)
sql
SELECT * FROM user_logs;插入、删除数据后执行这条语句,就能看到自动生成的操作日志。
3. 删掉触发器(写错需要重建时)
sql
DROP TRIGGER IF EXISTS tb_user_insert_trigger; DROP TRIGGER IF EXISTS tb_user_delete_trigger;关键知识点总结
时机
after insert/delete:数据操作完毕再记日志;一般日志都用 after新旧行关键字
新增:只有
NEW,没有 OLD删除:只有
OLD,没有 NEW修改:NEW(新数据)+ OLD(旧数据)都能用
concat():MySQL 字符串拼接函数,把多个文本、字段拼成一整条日志详情
注意你的代码小隐患
你没有写DELIMITER改结束符,在 Navicat/DataGrip 里大概率会报语法错误。
标准完整可运行写法示例
sql
DELIMITER // create trigger tb_user_insert_trigger after insert on tb_user for each row begin insert into user_logs(id, operation, operate_time, operate_id, operate_params) VALUES (null, 'insert', now(), new.id, concat('插入的数据内容为: id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=',new.email)); end // DELIMITER ;先把语句结束符临时改成//,避免触发器内部的;截断整段创建语句,创建完再改回默认;。
需要我给你一套完整测试 SQL(插入→删除→查看日志),你直接复制运行看效果吗?
和你刚刚写的区别在哪里
新旧两套触发器核心区别
一、最直观的 5 处差异
1. 日志存储内容复杂度不一样
我最开始给你的版本: 只简单记录:操作人 ID、时间、操作文字(新增用户),内容极简
sql
INSERT INTO user_logs(uid, operate_time, action) VALUES(NEW.id, NOW(), '新增用户');你现在这套: 把整条插入 / 删除的所有字段全部拼接打包存下来用concat()拼接 id、姓名、电话、邮箱、职业完整详情,日志更详细,方便后期回溯数据
sql
concat('插入的数据内容为: id=',new.id,',name=',new.name, ', phone=', NEW.phone, ', email=',new.email)2. 字段命名完全不同
我初始日志表字段:id、uid、operate_time、action
你自定义日志表字段:id、operation(操作类型)、operate_time(时间)、operate_id(用户id)、operate_params(完整参数详情)字段命名更贴合开发规范。
3. 新增了【删除触发器】
我一开始只写了INSERT 插入触发器你现在做了两套完整闭环:
插入后记录日志
删除后记录删除前原始数据日志 覆盖了新增、删除两种数据库操作场景。
4. 新旧关键字使用区分(核心语法区别)
1)插入场景: 两者都只用NEW(新数据),无区别 2)删除场景: 只能用OLD
OLD.id/ OLD.name:代表删掉之前原本存在的数据删除之后数据已经从表消失,只能靠 OLD 读取历史内容,这是新增触发器没有的语法点。
5. 语法细节:结束符 DELIMITER 问题
我最初完整示例里特意加了DELIMITER //修改语句终止符:
sql
DELIMITER // 触发器内容 end // DELIMITER ;作用:防止触发器内部的分号;提前截断创建语句,避免 1064 语法报错; 你截图里的代码没有加这段,在命令行 MySQL 里会直接语法报错,DataGrip 可视化工具做了兼容暂时没报错,但标准写法必须带上。
二、功能层面区别总结
表格
版本 | 支持操作 | 日志内容 | 适用场景 |
|---|---|---|---|
我最初简易版 | 仅插入 INSERT | 极简记录:用户 ID + 时间 + 操作文字 | 初学理解触发器原理 |
你现在完整版 | 插入 INSERT + 删除 DELETE | 完整存储整条数据详情 | 项目真实审计日志、数据恢复场景 |
三、额外小知识点
后续你还可以继续拓展UPDATE 修改触发器: 修改数据时,可以同时用OLD(修改前旧数据)+NEW(修改后新数据),记录数据前后变化。
四、查看日志的方式没有变化
无论日志内容简单还是详细,查看语句始终一致:
sql
SELECT * FROM user_logs;执行后就能看到插入、删除自动生成的全部操作记录。