使用CASE创建MySQL存储生成列报错,求问题原因排查
问题原因及解决方案
你遇到的这个错误,核心原因是MySQL的存储生成列(STORED)不支持在生成表达式中使用CASE这类复杂条件逻辑。当你创建或修改生成列时,如果没有明确指定VIRTUAL,部分MySQL版本会默认使用STORED(把计算结果持久化到磁盘),而存储生成列对表达式的限制非常严格:
- 表达式必须是确定性的(相同输入总能得到相同输出)
- 不能包含子查询、自定义存储函数、用户变量,也不支持
CASE这类分支逻辑(至少在部分版本中是这样)
解决办法一:改用虚拟生成列(VIRTUAL)
如果你的damage列不需要持久化存储,只是在查询时实时计算,把生成列类型改成VIRTUAL即可——虚拟生成列对表达式的限制宽松很多,完全支持CASE语句:
ALTER TABLE user_character MODIFY COLUMN damage BIGINT GENERATED ALWAYS AS ( 2*((`min_weapon_damage` + `max_weapon_damage`)/2) *(1 + ( CASE WHEN `character_class_id` = '1' THEN (`character_current_lvl` + `class_strength` + `trained_strength` + `strengthaddprefix` + (`character_current_lvl` + `class_strength` + `trained_strength` + `strengthaddprefix`) * `strengthmultiprefix` / 100) WHEN `character_class_id` = '2' THEN (`character_current_lvl` + `class_agility` + `trained_agility` + `agilityaddprefix` + (`character_current_lvl` + `class_agility` + `trained_agility` + `agilityaddprefix`) * `agilitymultiprefix` / 100) WHEN `character_class_id` = '3' THEN (`character_current_lvl` + `class_intelligence` + `trained_intelligence` + `intelligenceaddprefix` + (`character_current_lvl` + `class_intelligence` + `trained_intelligence` + `intelligenceaddprefix`) * `intelligencemultiprefix` / 100) END ) /10) ) VIRTUAL;
解决办法二:用触发器实现持久化计算(如果必须存储值)
如果业务上要求damage的值必须存在磁盘上(比如频繁查询需要性能优化),那可以把damage改成普通列,然后用触发器在插入或更新时自动计算赋值:
首先修改列类型为普通BIGINT:
ALTER TABLE user_character MODIFY COLUMN damage BIGINT;
然后创建插入触发器:
DELIMITER // CREATE TRIGGER calculate_damage_insert BEFORE INSERT ON user_character FOR EACH ROW BEGIN SET NEW.damage = 2*((NEW.min_weapon_damage + NEW.max_weapon_damage)/2) *(1 + ( CASE WHEN NEW.character_class_id = '1' THEN (NEW.character_current_lvl + NEW.class_strength + NEW.trained_strength + NEW.strengthaddprefix + (NEW.character_current_lvl + NEW.class_strength + NEW.trained_strength + NEW.strengthaddprefix) * NEW.strengthmultiprefix / 100) WHEN NEW.character_class_id = '2' THEN (NEW.character_current_lvl + NEW.class_agility + NEW.trained_agility + NEW.agilityaddprefix + (NEW.character_current_lvl + NEW.class_agility + NEW.trained_agility + NEW.agilityaddprefix) * NEW.agilitymultiprefix / 100) WHEN NEW.character_class_id = '3' THEN (NEW.character_current_lvl + NEW.class_intelligence + NEW.trained_intelligence + NEW.intelligenceaddprefix + (NEW.character_current_lvl + NEW.class_intelligence + NEW.trained_intelligence + NEW.intelligenceaddprefix) * NEW.intelligencemultiprefix / 100) END ) /10); END // DELIMITER ;
再创建更新触发器:
DELIMITER // CREATE TRIGGER calculate_damage_update BEFORE UPDATE ON user_character FOR EACH ROW BEGIN SET NEW.damage = 2*((NEW.min_weapon_damage + NEW.max_weapon_damage)/2) *(1 + ( CASE WHEN NEW.character_class_id = '1' THEN (NEW.character_current_lvl + NEW.class_strength + NEW.trained_strength + NEW.strengthaddprefix + (NEW.character_current_lvl + NEW.class_strength + NEW.trained_strength + NEW.strengthaddprefix) * NEW.strengthmultiprefix / 100) WHEN NEW.character_class_id = '2' THEN (NEW.character_current_lvl + NEW.class_agility + NEW.trained_agility + NEW.agilityaddprefix + (NEW.character_current_lvl + NEW.class_agility + NEW.trained_agility + NEW.agilityaddprefix) * NEW.agilitymultiprefix / 100) WHEN NEW.character_class_id = '3' THEN (NEW.character_current_lvl + NEW.class_intelligence + NEW.trained_intelligence + NEW.intelligenceaddprefix + (NEW.character_current_lvl + NEW.class_intelligence + NEW.trained_intelligence + NEW.intelligenceaddprefix) * NEW.intelligencemultiprefix / 100) END ) /10); END // DELIMITER ;
这样每次插入或更新数据时,damage列都会自动计算并赋值,效果和存储生成列类似,但绕过了生成列的表达式限制。
内容的提问来源于stack exchange,提问作者immeckro
相关产品推荐
相关产品推荐

