为何向无符号整数列插入负数未得到列类型最大值?
关于MySQL无符号整数溢出与截断的疑问解答
1. “此类操作”具体指什么?
文档中的“此类操作”特指导致结果超出无符号整数类型范围的运算或赋值操作,核心包含两种场景:
- 无符号整数参与的算术运算(尤其是减法)产生负数结果(比如
0 UNSIGNED - 1),默认会被转换为对应无符号类型的最大值(如BIGINT UNSIGNED的18446744073709551615)。 - 直接向无符号整数列插入低于其最小值(0)的数值(比如向
tinyint unsigned列插入-2)。
你提到的“整数间相减,若其中一个为UNSIGNED类型,默认结果为无符号值”是正确的,这类减法若得到负数,就属于触发截断规则的“此类操作”。
2. 为什么插入-2到tinyint unsigned列得到0而非254?
这是因为MySQL对直接插入字面量负数和无符号算术溢出结果的处理逻辑完全不同:
- 直接插入
-2这类字面量负数到无符号列时,MySQL判定为“值低于列的最小值(0)”,在默认sql_mode(不含严格模式)下,会将值截断到列的最小值0——这和C语言的补码循环转换逻辑不同。 - 只有插入无符号算术运算的溢出结果时,才会触发“截断为列类型最大值”的规则。比如
CAST(0 AS UNSIGNED) - 1的结果是BIGINT UNSIGNED的最大值18446744073709551615,插入tinyint unsigned列时,MySQL会取该值的低8位(二进制全1),最终得到tinyint unsigned的最大值255,而非你预期的254。
3. “结果被截断为该列类型的最大值”的具体含义
这条规则仅针对无符号算术运算产生的溢出值(即负数被转换为超大无符号值的情况):
- 当运算结果是大尺寸无符号类型(如BIGINT UNSIGNED)且超出目标无符号列范围时,MySQL会对结果进行按位截断,取与目标列位数一致的低N位,最终得到的值就是目标列的最大值(比如tinyint unsigned是8位,低8位全1对应255)。
- 若启用
NO_UNSIGNED_SUBTRACTION,无符号减法的结果会被视为有符号负数,此时插入无符号列会截断到0,而非最大值。
4. 你的测试问题排查
你在默认sql_mode下的测试结果符合MySQL规则:
INSERT INTO test (uti) VALUES (-2);得到0:属于直接插入低于最小值的数值,触发最小值截断。INSERT INTO test (uti) VALUES ((SELECT CAST(0 AS UNSIGNED) - 1));应得到255而非溢出报错:默认sql_mode下无符号算术溢出不会报错,而是转换为超大无符号值,插入时按位截断到列的最大值。如果你的结果不符,检查是否开启了严格模式(如STRICT_TRANS_TABLES),此时会直接报错而非截断。
内容的提问来源于stack exchange,提问作者OnlyWick
相关产品推荐
相关产品推荐

