You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel迁移中SQLite环境修改主键的非冗余实现方案

问题说明
  • 最初创建items表的迁移使用UUID类型的id作为主键,代码如下:
Schema::create('items', function(Blueprint $table) {
    $table->uuid('id')->primary();
    // 其余字段定义...
});
  • 后续迭代需要新增自增主键列,一开始写的结构调整迁移代码如下:
Schema::table('items', function(Blueprint $table) {
    $table->dropPrimary('id');
    $table->rename('id', 'SystemId')->change();
    $table->id();
});
  • 执行迁移时直接失败:SQLite不支持直接修改表主键。官方文档给出的兼容方案是删除原表后按新结构重建,但这种写法需要把首次迁移里的全量表结构代码复制一遍,完全违反DRY原则,需要更优雅的实现路径。
可行实现方案

SQLite底层不支持主键修改的限制是绕不开重建步骤的,但完全没必要硬编码复制所有表结构,两种可落地的方案都能避免重复代码:

方案一:动态读取表结构完成重建,零硬编码重复字段

通过Schema门面提供的字段读取能力,自动获取原表的字段、属性,动态生成新表结构,不需要手动写任何原有字段的定义:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;

return new class extends Migration
{
    public function up()
    {
        $originTable = 'items';
        $tempTable = 'items_temp';

        // 创建符合新结构的临时表
        Schema::create($tempTable, function (Blueprint $table) use ($originTable) {
            // 先定义调整后的主键结构
            $table->id();
            $table->uuid('SystemId')->unique();

            // 自动读取原表所有字段,跳过原id字段,其余字段自动按原结构生成
            $originColumns = Schema::getColumnListing($originTable);
            foreach ($originColumns as $column) {
                if ($column === 'id') continue;
                $columnType = DB::getSchemaBuilder()->getColumnType($originTable, $column);
                $table->{$columnType}($column)->nullable();
            }
        });

        // 把原表数据迁移到临时表,原id值映射到SystemId字段
        $dataColumns = array_filter(Schema::getColumnListing($originTable), fn($col) => $col !== 'id');
        DB::statement("INSERT INTO $tempTable (SystemId, " . implode(',', $dataColumns) . ") SELECT id AS SystemId, " . implode(',', $dataColumns) . " FROM $originTable");

        // 替换原表
        Schema::drop($originTable);
        Schema::rename($tempTable, $originTable);
    }

    public function down()
    {
        // 回滚逻辑反向操作即可
        $originTable = 'items';
        $tempTable = 'items_temp';

        Schema::create($tempTable, function (Blueprint $table) use ($originTable) {
            $table->uuid('id')->primary();
            $originColumns = Schema::getColumnListing($originTable);
            foreach ($originColumns as $column) {
                if (in_array($column, ['id', 'SystemId'])) continue;
                $columnType = DB::getSchemaBuilder()->getColumnType($originTable, $column);
                $table->{$columnType}($column)->nullable();
            }
        });

        $dataColumns = array_filter(Schema::getColumnListing($originTable), fn($col) => !in_array($col, ['id', 'SystemId']));
        DB::statement("INSERT INTO $tempTable (id, " . implode(',', $dataColumns) . ") SELECT SystemId AS id, " . implode(',', $dataColumns) . " FROM $originTable");

        Schema::drop($originTable);
        Schema::rename($tempTable, $originTable);
    }
};

如果表存在自定义索引、外键约束,读取字段时同步拉取索引、外键定义,在临时表创建时自动加上即可,全程不需要手动维护原有结构代码。

方案二:安装doctrine/dbal依赖,让Laravel自动处理重建

Laravel的SQLite驱动本身内置了不兼容DDL的自动兼容逻辑:遇到SQLite不支持的表修改操作时,框架会自动完成临时表创建、数据迁移、表替换的全流程,只需要安装依赖后调整迁移的执行顺序即可,不需要自己写重建逻辑:

  1. 先安装必要依赖:
    composer require doctrine/dbal
  2. 调整迁移代码,拆分操作步骤避免主键冲突:
// 第一步:先加临时字段存储新主键值
Schema::table('items', function(Blueprint $table) {
    $table->unsignedBigInteger('new_id')->nullable();
});

// 第二步:给新主键填充自增初始值
DB::unprepared('SET @row_num = 0');
DB::unprepared('UPDATE items SET new_id = (@row_num := @row_num + 1)');

// 第三步:执行字段重命名、主键调整,框架会自动处理SQLite兼容
Schema::table('items', function(Blueprint $table) {
    $table->renameColumn('id', 'SystemId');
    $table->renameColumn('new_id', 'id');
    $table->primary('id');
    $table->bigIncrements('id')->change();
});
注意事项
  • 执行迁移前务必备份数据库,避免操作失误导致数据丢失
  • 如果表数据量超过百万级,重建表的锁表时间会比较长,建议在业务低峰期执行
  • 存在外键关联的表,迁移完成后记得同步更新关联表的外键类型和关联约束

内容的提问来源于stack exchange,提问作者shaedrich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 13:03:20