建表时必须显式声明INT UNSIGNED,GUI勾选可能不生成对应SQL;修改字段需用ALTER TABLE MODIFY/CHANGE;PHP传值须确保在0–4294967295范围内,优先用64位环境及PDO::PARAM_STR避免溢出。
MySQL 建表时怎么让 INT 字段变成 UNSIGNED
直接在类型后面加 unsigned,不是勾选框,也没“属性勾选”这回事——这是图形化工具(比如 navicat、dbeaver)界面里模拟出来的操作,底层仍是 sql 语句。
常见错误是以为只要在 GUI 里点一下“无符号”就完事了,结果导出 SQL 或换环境部署时字段悄悄变回有符号,导致插入大数时报 Out of range value for column。
- 建表时写死:用
INT UNSIGNED,不是INT后面再加个开关 - 修改已有字段:必须用
ALTER TABLE ... MODIFY COLUMN或CHANGE COLUMN,只改类型不改其他属性也得重写整条定义 - 注意默认值:
INT UNSIGNED的默认最小值是0,如果原字段有负数默认值(比如DEFAULT -1),会直接报错
PHP MySQLi/PDO 插入 UNSIGNED INT 字段报错怎么办
不是 PHP 限制了无符号,而是你传了个超范围的值过去,或者用了带符号的整型变量去赋值——PHP 自身没有 unsigned int 类型,所有 int 都是有符号的,但数据库能存,PHP 只要传的数值在 0 到 4294967295(对应 INT UNSIGNED)之间就行。
典型现象:mysqli_sql_exception: Out of range value for column 'id',其实是因为 PHP 把一个大于 2147483647 的正数当成了负数处理(比如 3000000000 在 32 位 int 下溢出为负),再塞进数据库时被截断或拒绝。
- 检查 PHP 运行环境是否为 64 位:用
PHP_INT_SIZE确认,64 位下PHP_INT_MAX是9223372036854775807,能安全表示INT UNSIGNED全范围 - 避免隐式转换:不要用
(int)强转字符串大数,改用filter_var($str, FILTER_VALIDATE_INT)或直接保持字符串传参(PDO 支持) - PDO 预处理时,用
PDO::PARAM_INT没问题,但前提是 PHP 变量本身数值合法;更稳妥的是用PDO::PARAM_STR避开整型解释
MySQL 中 TINYINT/SMALLINT/BIGINT 的 UNSIGNED 怎么选
选哪个,取决于你要存的数有多大,以及是否真需要省那几个字节——别为了“看着整齐”全设成 UNSIGNED,否则以后要存负数就得改表,还可能引发应用层逻辑错乱。
容易踩的坑是:把 TINYINT UNSIGNED 当布尔用(0/1),结果某天业务要加个状态码 2,发现字段上限卡在 255 还好说,但要是误设成 TINYINT(有符号),最大只能到 127,128 就溢出了。
-
TINYINT UNSIGNED:范围 0–255,适合状态码、类型标识等小枚举 -
SMALLINT UNSIGNED:0–65535,适合地区编码、小规模 ID 映射 -
BIGINT UNSIGNED:0–18446744073709551615,适合分布式主键、计数器(如 PV)、时间戳微秒值 - 性能影响几乎为零,但排序、索引大小会略增(因值域变宽,实际存储长度不变,但比较逻辑稍复杂)
Navicat/DBeaver 里“无符号”勾选后生成的 SQL 对不对
不一定。有些老版本 Navicat 在“编辑表结构”里勾了 Unsigned,但导出 DDL 时漏掉 UNSIGNED 关键字,或者把 INT(11) UNSIGNED 错写成 INT(11) ——括号里的数字(显示宽度)和 UNSIGNED 完全无关,删掉也不影响存储。
最保险的做法:右键表 → “查看表 SQL”,逐字核对生成语句里有没有 UNSIGNED;或者干脆不用 GUI 生成功能,手写 CREATE TABLE 并存为版本化 SQL 文件。
- 检查导出 SQL 中字段定义是否含
UNSIGNED,而不是只看界面勾选状态 - 留意工具是否启用了“兼容模式”或“MySQL 5.6 兼容”,某些设置会自动过滤掉非标准关键字
- 如果用 Liquibase/Flyway 等迁移工具,它们通常不识别 GUI 导出的“扩展属性”,只认标准 SQL,所以务必人工确认
真正麻烦的不是设不设 UNSIGNED,而是设了之后没同步更新应用层的数据校验、ORM 映射、前端输入限制——比如前端 still 允许用户输入负数,后端没做范围判断,最后数据库替你拦住,但错误信息根本不会告诉你“你该改前端”。

















