问题
今天学习触发器的使用,在练习到update的触发器时,发现当更新数据时如果原来表中的旧值含有空字段,那么就无法填充信息字符串到日志表的指定列中,报错信息为:
[23000][1048] Column 'operation_params' cannot be null
问题分析:
1.我创建的触发器被触发时要更新的日志表,语句结构为:
CREATE TABLE `user_log` (
`id` int NOT NULL AUTO_INCREMENT,
`operation` varchar(20) NOT NULL,
`operation_id` int NOT NULL,
`operation_params` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
原因1分析:其实结合报错和这个表结构可以知道user_log表中的operation_params值不能为null所导致的。
2.我创建的触发器语句为:
# 所谓的行级触发器,也就是每更新一行就会触发一次,即触发器中声明的 after/before insert/update/delete on 表名 for each row
create trigger update_user_log
after update on indexUsers for each row
begin
insert into user_log values
(null,'update',old.id,concat('更新前的行id为:',old.id,'名字:',old.name,'专业为:',old.profession,'年龄为:',old.age,'____','更新后行id为:',new.id
,'名字为:',new.name,'专业为:',new.profession,'年龄为:',new.age));
end;
3.触发器监听的indexUsers表结构大概如下,这里就不贴代码了:
可以看到indexUsers表中存在一部分数据的name字段等值为空,当第2步创建的update_user_log触发器被触发时,如果修改的是id为987-1235的行数据,执行该触发器的插入字段到user_log中时,old.name字段值必然为空,(触发器如果要显示更新前后的字段值,可以使用old和new对象,old包含被触发行修改前的所有字段值,new包含被触发行所有被触发器修改后的字段值)
验证执行update语句
update indexUsers set name='小红',profession='礼仪',age=28 where id = 1235;
就会触发上面我们定义好的触发器插入数据到user_log表中的错误:
错误的解决办法:
如果是因为user_log表中的operation_params列不能为空而导致日志信息无法正常插入,是不是可以修改此列值允许为空就可以解决了呢?
可以执行修改user_log表中的operation_params字段允许为空的sql:
alter table user_log modify operation_params varchar(255);
重新执行update语句触发更新触发器后,不报错了,但是查询日志表user_log的信息可以看到,该列的信息值只有一个null,和我们想要的如果更新前字段值为null显示null,不为null就正常显示的日志相悖:
问题真实原因
后面查阅了不少资料,了解到了concat聚合函数拼接字符串时,如果其中一个值为null,那么concat聚合函数返回的整个字符串就为null值! 这里和前端后端不太一样,任何和字符串拼接的值都会变成字符串,所以触发器在插入数据到user_log日志表时才会导致报错,或者返回一个null给该字段。
验证结果如下:
解决办法
知道了是concat聚合函数造成拼接字符串造成的,可以使用ifnull进行解决concat的拼接问题,使用方法如下:
1.当ifnull表达式的第一个参数值为null时,那么ifnull表达式就会返回第二个参数值
select concat('hello',ifnull(null,'null'),'456');
2.当ifnull表达式的字段值1不为null时,直接使用字段值1进行拼接:
通过以上例子,就可以去修改触发器中的concat语句了。
先删除之前user_log插入的null行,恢复user_log表的operation_params列不为空:
alter table user_log modify operation_params varchar(255) not null;
从而保证每一次触发器中的信息后续插入时都不会只显示一个null,并且能够正常插入
修改后的触发器如下:
create trigger update_user_log
after update on indexUsers for each row
begin
insert into user_log values
(null,'update',old.id,concat('更新前的行id为:',old.id,'名字:',ifnull(old.name,'null'),'专业为:',ifnull(old.profession,'null'),'年龄为:',ifnull(old.age,'null'),'____','更新后行id为:',new.id
,'名字为:',ifnull(new.name,'null'),'专业为:',ifnull(new.profession,'null'),'年龄为:',ifnull(new.age,'null')));
end;
在执行修改后的update_user_log触发器前先删除原来的update_user_log触发器:
drop trigger if exists update_user_log;
之后执行上方修改后的触发器sql语句,并更新id为987的这一行做测试:
update indexUsers set name='小红',profession='礼仪',age=28 where id = 987;
问题解决!可以看到,更新前为null的name字段在user_log日志表中可以显示为null,其他需要显示的非null字段也正常显示
总结
本次触发器所遇到的更新日志表时报错[23000][1048] Column ‘operation_params’ cannot be null,原因为concat聚合函数的使用不够熟悉,与以往学习的前端开发拼接字符串有区别,concat拼接字符时如果遇到了null会直接整体返回null,前端其他值和字符串拼接往往都会变为字符串。如果想解决null值显示的问题,可以在concat聚合函数中使用ifnull()表达式,里面传两个值,当值1为真时返回值1进行拼接,不为真返回值2进行拼接。