laravel 12 迁移 postgresql 的核心是连接切换、类型适配和 sql 行为对齐:需启用 pdo_pgsql、安装 doctrine/dbal ^3.7,修改 .env 驱动为 pgsql;迁移中将 tinyint 改 boolean、用 json() 映射 jsonb;查询时严格 group by、统一用 || 拼接字符串、显式指定 nulls last,并推荐 pgloader 进行数据迁移。

直接改配置+调语法,不是重写代码。Laravel 12 对 PostgreSQL 支持成熟,迁移核心是三件事:连接切换、类型适配、SQL 行为对齐。
一、环境与驱动准备
确保 PHP 已启用 pdo_pgsql 扩展(非 pdo_mysql)。Laravel 12 默认要求 doctrine/dbal ^3.7,运行:
composer require doctrine/dbal:^3.7- 在
.env中切换驱动:DB_CONNECTION=pgsqlDB_HOST=127.0.0.1DB_PORT=5432DB_DATABASE=your_dbDB_USERNAME=postgresDB_PASSWORD= - 执行
php artisan config:clear清除缓存,再用php artisan tinker验证:DB::connection()->getPdo()不报错即连通
二、迁移文件兼容性处理
Laravel 的迁移语法本身跨库友好,但 PostgreSQL 对约束和默认值更严格:
-
$table->id()在 PostgreSQL 中生成SERIAL,完全可用;若需显式控制,改用$table->bigInteger('id')->primary()->autoIncrement() -
TINYINT(1)类型(常被当布尔用)需替换为$table->boolean('is_active'),避免映射成SMALLINT后应用层解析异常 -
JSON字段直接用$table->json('metadata'),PostgreSQL 自动映射为JSONB,支持索引(后续可加->index('metadata', 'gin')) - 日期字段建议统一用
$table->timestampsTz(),确保created_at存为TIMESTAMPTZ,避免时区丢失
三、运行时 SQL 行为适配
真正卡住业务的往往不是建表,而是查询逻辑。以下几处必须检查:
-
GROUP BY 严格模式:MySQL 允许
SELECT name, age FROM users GROUP BY name,PostgreSQL 报错。Laravel 查询构造器中需显式写出所有非聚合列,或改用->selectRaw('name, MAX(age) as age') -
字符串拼接:MySQL 用
CONCAT(a,b),PostgreSQL 原生推荐a || b;若坚持用函数,需确认已启用pg_trgm或保留CONCAT()(9.6+ 支持) -
空值排序:MySQL 默认
NULLS LAST,PostgreSQL 默认NULLS FIRST。如需一致行为,在orderBy()后加->orderByRaw('updated_at DESC NULLS LAST') -
分页偏移量位置:虽然 Laravel 构造器自动适配,但手写原生查询时注意
LIMIT 10 OFFSET 20在 PG 中等效,无需调整顺序
四、数据迁移与验证
不建议用 mysqldump + psql 直导——类型和约束易出错。推荐组合方案:
- 用
pgloader全量迁移(支持类型映射、索引重建、外键自动处理):pgloader mysql://user:pass@localhost/myapp postgresql://user:pass@localhost/myapp_pg - 迁移后立即校验:
查行数:SELECT COUNT(*) FROM users;两库比对
抽样查字段值(尤其 JSON、时间、布尔字段)
运行php artisan migrate:fresh --seed确保新结构下 Seeder 能跑通 - 开启
log_queries = true和慢日志,上线初期捕获隐式类型转换或缺失索引问题











