SHOW CREATE TABLE 更可靠,因其返回 MySQL 实际解析的 DDL,精确包含类型、长度、NULL 性、默认值等,避免 mysqldump --no-data 对 TINYINT(1) 误判及冗余信息干扰。
导出 MySQL 表结构时 SHOW CREATE TABLE 为什么比 mysqldump --no-data 更可靠
因为 mysqldump --no-data 默认会添加引擎、字符集、注释等冗余信息,且对 tinyint(1) 这类“伪布尔”类型可能误判为布尔字段,laravel 迁移里写成 $table->boolean() 就会丢掉长度和实际含义;而 show create table 返回的是 mysql 实际解析后的 ddl,字段类型、括号内长度、是否允许 null、默认值全在,直接对照抄进 schema::create() 更少歧义。
实操建议:
- 用
mysql -u root -p -e "SHOW CREATE TABLE users\G"(注意\G让输出可读) - 复制
CREATE TABLE块中id到) ENGINE=之前的部分,忽略末尾的ENGINE、DEFAULT CHARSET等行 - 把
int(10) unsigned拆成 Laravel 对应写法:$table->unsignedInteger('id')->length(10)(注意:Laravel 9+ 才支持->length(),旧版需手动注释说明)
Laravel 迁移里 tinyint(1) 到底该用 boolean() 还是 tinyInteger()
取决于原始字段是不是真用来存布尔逻辑。MySQL 的 TINYINT(1) 本质就是 1 字节整数,(1) 只影响显示宽度,不约束取值范围——它完全可以存 0/1/2/127。如果原表用它存状态码(比如 0=待审核, 1=通过, 2=拒绝),用 $table->boolean() 会丢失语义,迁移后查出来全是 true/false,PHP 层逻辑就崩了。
实操建议:
- 查原始数据:
SELECT DISTINCT status FROM orders;,如果结果不止 0 和 1,必须用$table->tinyInteger('status') - 若确认只存 0/1 且语义就是布尔,Laravel 会把
boolean()映射为TINYINT(1),但记得加注释:// maps to TINYINT(1) in MySQL, not BIT - 别依赖
->nullable()自动推断默认值;TINYINT字段若原表有DEFAULT '0',迁移里得显式写->default(0)
varchar(191) 和 string() 长度对不上?别硬套,看 MySQL 版本和排序规则
Laravel 默认 $table->string('email') 生成 VARCHAR(255),但如果你的 MySQL 是 5.7 + utf8mb4_unicode_ci,255 字符可能超索引限制(InnoDB 单索引列最大 767 字节,utf8mb4 最坏占 4 字节/字符 → 767 ÷ 4 = 191)。所以原库若设了 VARCHAR(191),不是拍脑袋,是为兼容索引。
实操建议:
- 导出前先查排序规则:
SHOW CREATE TABLE posts;看COLLATE值,是utf8mb4_*就按 191 控制;若是utf8_general_ci,可用 255 - Laravel 迁移里写
$table->string('slug', 191),别省略第二个参数,否则默认 255,上线建索引失败 - 如果字段不需要索引(比如长文本摘要),且确定不会搜索,可放宽到 255,但得在注释里写清原因
时间字段导出后发现 datetime 和 timestamp 在 Laravel 里行为不同
MySQL 的 TIMESTAMP 自动时区转换、自动初始化,DATETIME 则完全按字面存储。Laravel 的 $table->timestamps() 默认用 TIMESTAMP,但如果你导出的原表是 DATETIME(比如 legacy 系统统一用 UTC 存储、应用层处理时区),直接套用会出问题——比如 PHP 写入 2024-05-01 12:00:00,MySQL 可能存成 2024-05-01 20:00:00(因服务器时区是 UTC+8)。
实操建议:
- 导出时重点看字段定义里的类型关键字,不是只看名字;
created_at字段若定义为DATETIME DEFAULT CURRENT_TIMESTAMP,就不是标准 Laravel 时间戳行为 - 对应迁移写法:
$table->dateTime('created_at')->useCurrent();(useCurrent()替代useCurrentOnUpdate(),避免自动更新) - 如果原表
updated_at用的是触发器或应用层更新,迁移里就别加->useCurrentOnUpdate(),否则会覆盖原逻辑
最麻烦的其实是复合场景:比如一个字段既是 TINYINT(1) 又带 DEFAULT b'1'(二进制字面量),这种 MySQL 特有语法 Laravel 根本不识别,只能手动处理成整数默认值,并在迁移文件顶部加注释说明来源。









