求助:Sequelize+MySQL+NestJS迁移时外键字段不兼容错误
使用Sequelize + MySQL + NestJS进行数据库迁移时,新增关联表时触发外键约束不兼容错误。
新表table_1的迁移代码:
import { DataType, Sequelize } from 'sequelize-typescript' export async function up(queryInterface: QueryInterface) { await queryInterface.createTable('table_1', { id: { type: DataType.STRING, primaryKey: true, }, createdAt: { type: DataType.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), allowNull: false, }, updatedAt: { type: DataType.DATE, defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), allowNull: false, }, }) } export async function down(queryInterface: QueryInterface) { await queryInterface.dropTable('table_1') }
关联表table_2的迁移代码:
import { DataType } from 'sequelize-typescript' export async function up(queryInterface: QueryInterface) { const transaction = await queryInterface.sequelize.transaction() try { await queryInterface.addColumn( 'table_2', 'column_1', { type: DataType.STRING, }, { transaction } ) await queryInterface.addConstraint('table_2', ['column_1'], { type: 'foreign key', onDelete: 'cascade', onUpdate: 'cascade', references: { table: 'table_1', field: 'id', }, transaction, }) await transaction.commit() } catch (err) { await transaction.rollback() throw err } } export async function down(queryInterface: QueryInterface) { const transaction = await queryInterface.sequelize.transaction() try { await queryInterface.removeConstraint('table_2', 'column_1', { transaction, }) await queryInterface.removeColumn('table_2', 'column_1', { transaction, }) await transaction.commit() } catch (err) { await transaction.rollback() throw err } }
执行迁移时出现错误:
ERROR: Referencing column 'column_1' and referenced column 'id' in foreign key constraint 'table_1_column_1_table_2_fk' are incompatible.
明明id和column_1都使用了DataType.STRING,却提示类型不兼容,该如何解决?
这个问题的核心是MySQL的外键约束要求关联字段不仅基础类型一致,字符集、排序规则(collation)、长度限制也必须完全匹配。Sequelize的DataType.STRING只是基础类型声明,默认参数会导致实际生成的数据库字段属性存在差异。
具体修复步骤:
统一字段的完整属性
修改table_1的id字段定义,明确指定长度、字符集和排序规则,同时给table_2的column_1设置完全相同的属性:更新
table_1的迁移代码:id: { type: DataType.STRING(36), // 明确长度,比如UUID常用36位,根据业务调整 primaryKey: true, charset: 'utf8mb4', collate: 'utf8mb4_unicode_ci', },更新
table_2的column_1字段定义:await queryInterface.addColumn( 'table_2', 'column_1', { type: DataType.STRING(36), charset: 'utf8mb4', collate: 'utf8mb4_unicode_ci', allowNull: true, // 根据业务需求调整,外键字段通常允许null或设为not null }, { transaction } )确认迁移执行顺序
Sequelize会根据迁移文件名的前缀时间戳排序执行,必须保证创建table_1的迁移文件在修改table_2的迁移文件之前执行。如果顺序颠倒,创建外键时table_1还不存在,也会触发类似错误。修复down方法的约束名称错误
当前down方法中直接用column_1作为约束名称是错误的,MySQL自动生成的外键约束名并非字段名。建议在addConstraint时显式指定约束名称,方便后续准确删除:修改
table_2的up方法:await queryInterface.addConstraint('table_2', ['column_1'], { type: 'foreign key', name: 'table_2_column_1_fk', // 自定义约束名 onDelete: 'cascade', onUpdate: 'cascade', references: { table: 'table_1', field: 'id', }, transaction, })对应的down方法:
await queryInterface.removeConstraint('table_2', 'table_2_column_1_fk', { transaction, })验证数据库字段实际属性
如果修改后仍有问题,直接登录MySQL查看两个字段的实际属性:SHOW FULL COLUMNS FROM table_1 LIKE 'id'; SHOW FULL COLUMNS FROM table_2 LIKE 'column_1';对比
Type、Collation等字段,确保属性完全一致。
内容的提问来源于stack exchange,提问作者Matgorn

