MySQL存储过程与触发器实战技巧
存储过程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 ageuage; -- 第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记录删除前的数据 用到两个关键字NEWINSERT/UPDATE 触发时代表新增 / 修改后的新数据行OLDDELETE/UPDATE 触发时代表删除 / 修改前的旧数据行第一段插入触发器 tb_user_insert_triggersqlcreate 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 rowafter insert插入 tb_user 数据完成之后再执行触发器for each row每插入一行数据就触发一次begin ... end触发器要执行的 SQL 逻辑体内部插入日志逻辑sqlinsert into user_logs(字段) values(值)表格字段填入内容含义idnull日志主键自增填 null 自动生成operationinsert操作类型新增operate_timenow()当前系统时间operate_idnew.id本次新增用户的 idNEW 刚插入的新行数据operate_paramsconcat 拼接字符串把本次插入的所有字段打包成文本存起来触发场景执行插入语句sqlINSERT INTO tb_user(id,name,phone,email) VALUES(1,张三,13800138000,zsqq.com);tb_user 插入成功瞬间自动在user_logs生成一条插入日志。第二段删除触发器 tb_user_delete_triggersqlcreate 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 把删除前所有字段拼接保存方便后期回溯找回删掉的数据触发场景执行删除语句sqlDELETE FROM tb_user WHERE id1;数据删掉之后自动写入一条删除日志留存删除前完整信息。配套查看命令1. 查看当前库所有触发器你执行的 show triggers;sqlshow triggers;运行后表格会列出触发器名称、触发时机、绑定表、触发语句等信息能验证两个触发器都创建成功。2. 查看日志记录验证触发器有没有生效sqlSELECT * FROM user_logs;插入、删除数据后执行这条语句就能看到自动生成的操作日志。3. 删掉触发器写错需要重建时sqlDROP 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 里大概率会报语法错误。标准完整可运行写法示例sqlDELIMITER // 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、时间、操作文字新增用户内容极简sqlINSERT INTO user_logs(uid, operate_time, action) VALUES(NEW.id, NOW(), 新增用户);你现在这套 把整条插入 / 删除的所有字段全部拼接打包存下来用concat()拼接 id、姓名、电话、邮箱、职业完整详情日志更详细方便后期回溯数据sqlconcat(插入的数据内容为: 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删除场景 只能用OLDOLD.id/ OLD.name代表删掉之前原本存在的数据删除之后数据已经从表消失只能靠 OLD 读取历史内容这是新增触发器没有的语法点。5. 语法细节结束符 DELIMITER 问题我最初完整示例里特意加了DELIMITER //修改语句终止符sqlDELIMITER // 触发器内容 end // DELIMITER ;作用防止触发器内部的分号;提前截断创建语句避免 1064 语法报错 你截图里的代码没有加这段在命令行 MySQL 里会直接语法报错DataGrip 可视化工具做了兼容暂时没报错但标准写法必须带上。二、功能层面区别总结表格版本支持操作日志内容适用场景我最初简易版仅插入 INSERT极简记录用户 ID 时间 操作文字初学理解触发器原理你现在完整版插入 INSERT 删除 DELETE完整存储整条数据详情项目真实审计日志、数据恢复场景三、额外小知识点后续你还可以继续拓展UPDATE 修改触发器 修改数据时可以同时用OLD修改前旧数据NEW修改后新数据记录数据前后变化。四、查看日志的方式没有变化无论日志内容简单还是详细查看语句始终一致sqlSELECT * FROM user_logs;执行后就能看到插入、删除自动生成的全部操作记录。