MySQL WHERE子句比较年份触发BIGINT UNSIGNED溢出如何解决
问题描述
我有一张名为links的表,用于关联父子两类分类,该表共有5个字段:
- autoinc
- parent_category_name year(4) NOT NULL
- parent_category_year varchar(255) NOT NULL
- child_category_name year(4) NOT NULL
- child_category_year varchar(255) NOT NULL
我需要编写INSERT SELECT语句查询获取父子分类的category_id,将其插入临时表,编写的初始查询语句如下:
INSERT INTO temp (parent_category_id, child_category_id) SELECT parent.parent_category_id, child.child_category_id FROM links JOIN categories AS parent ON parent.name = link.parent_category_name AND parent.year = link.parent_category_year JOIN categories AS child ON child.name = link.child_category_name AND child.year = link.child_category_year
该查询运行正常,但我需要新增以下业务规则:
- 父分类年份与子分类年份相等
或 - 父分类年份比子分类年份小1
我为查询新增了如下WHERE子句:
WHERE link.child_category_year = link.parent_category_year OR link.child_category_year - link.parent_category_year = 1
但该INSERT语句在Perl代码中执行时抛出如下异常:
DBI Exception: DBD::mysql::db do failed: BIGINT UNSIGNED value is out of range in '(`my_database`.`links`.`child_category_year` - `my_database`.`links`.`parent_category_year`)' [for Statement "
原因分析
报错的核心原因是年份字段为*无符号整数(UNSIGNED)*类型,当父分类年份大于子分类年份时,child_category_year - parent_category_year的运算结果为负数,无符号类型无法存储负数,就会触发数值溢出报错。
解决方案
这里提供两种可行方案,优先推荐第二种,无需类型转换、性能更高:
方案1:将字段转为有符号整数后运算
通过CAST函数将无符号的年份字段转为有符号整数,再做减法运算,避免溢出:
WHERE link.child_category_year = link.parent_category_year OR CAST(link.child_category_year AS SIGNED) - CAST(link.parent_category_year AS SIGNED) = 1
方案2:调整判断逻辑,完全避免减法运算
将「子年份减父年份等于1」的判断等价转换为「子年份等于父年份加1」,从根源上避免出现负数运算,性能更优:
WHERE link.child_category_year = link.parent_category_year OR link.child_category_year = link.parent_category_year + 1
内容的提问来源于stack exchange,提问作者ComputersAreNeat
相关产品推荐
相关产品推荐

