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

迁移脚本中字符串比较如何使用相同排序规则?

解决MySQL排序规则不兼容导致的Liquibase迁移错误

问题场景

应用部署在多数据库环境,使用Liquibase编写迁移脚本。近期的迁移逻辑是将大表ExampleTable的varchar列exampleName迁移至独立表NameTable,并改用引用字段exampleName_id替代。本地测试(包含MySQL环境)均正常,但在某台MySQL机器上执行迁移时触发排序规则不兼容错误:

Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='
[Failed SQL: (1267) UPDATE myschema.ExampleTable SET exampleName_id = (SELECT n.id FROM NameTable n WHERE n.name = ExampleTable.exampleName)]

问题原因

新表NameTable.name的排序规则与原表ExampleTable.exampleName不一致,推测是MySQL版本升级导致默认排序规则变化。虽然显式指定排序规则能临时解决问题,但存在两个弊端:无法提前知晓各机器的列排序规则,且脚本无法跨SQL方言通用。

现有迁移脚本

<createTable tableName="NameTable">
    <column name="id" type="${type.bigint}" autoIncrement="true">
        <constraints primaryKey="true" nullable="false" />
    </column>
    <column name="name" type="varchar(255)">
        <constraints nullable="false" />
    </column>
</createTable>

<addUniqueConstraint tableName="NameTable" constraintName="unique_name" columnNames="name" />

<sql>INSERT INTO NameTable (name) SELECT DISTINCT exampleName FROM ExampleTable</sql>

<addColumn tableName="ExampleTable">
    <column name="exampleName_id" type="${type.bigint}"
            valueComputed="(SELECT n.id FROM NameTable n WHERE n.name = ExampleTable.exampleName)" />
</addColumn>

<addNotNullConstraint tableName="ExampleTable" columnName="exampleName_id" columnDataType="${type.bigint}" />

<addForeignKeyConstraint constraintName="FK_ExampleTable_exampleName_id" 
                         baseTableName="ExampleTable" baseColumnNames="exampleName_id"
                         referencedTableName="NameTable" referencedColumnNames="id" />

<dropColumn tableName="ExampleTable" columnName="exampleName" />

尝试的临时方案

通过显式指定排序规则规避错误,但无法适配多环境:

<addColumn tableName="ExampleTable">
    <column name="exampleName_id" type="${type.bigint}"
            valueComputed="(SELECT n.id FROM NameTable n WHERE n.name = ExampleTable.exampleName COLLATE utf8mb4_unicode_ci)" />
</addColumn>

可行解决方案

1. 创建新表时继承原列的排序规则(推荐)

通过查询information_schema动态获取原列的排序规则,创建NameTable时直接沿用,从根源避免不兼容问题:

<!-- 替换原createTable逻辑 -->
<sql>
CREATE TABLE NameTable (
    id ${type.bigint} AUTO_INCREMENT PRIMARY KEY NOT NULL,
    name varchar(255) NOT NULL COLLATE (SELECT collation_name FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'ExampleTable' AND column_name = 'exampleName')
);
</sql>

<addUniqueConstraint tableName="NameTable" constraintName="unique_name" columnNames="name" />

该方案仅针对MySQL生效,其他数据库会自动忽略COLLATE子句(或无需处理),不影响跨方言兼容性。

2. 查询时动态匹配原列排序规则

如果不想修改表结构,可在关联查询的SQL中动态获取原列排序规则,确保比较时使用一致规则:

<addColumn tableName="ExampleTable">
    <column name="exampleName_id" type="${type.bigint}"
            valueComputed="(SELECT n.id FROM NameTable n WHERE n.name = ExampleTable.exampleName COLLATE (SELECT collation_name FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'ExampleTable' AND column_name = 'exampleName'))" />
</addColumn>

3. Liquibase属性适配(可选)

通过Liquibase的数据库专属属性,针对MySQL动态设置排序规则,兼顾脚本可读性和兼容性:

<!-- 定义MySQL专属的排序规则属性 -->
<property name="mysql.name.collation" 
          value="COLLATE (SELECT collation_name FROM information_schema.columns WHERE table_schema = DATABASE() AND table_name = 'ExampleTable' AND column_name = 'exampleName')" 
          dbms="mysql"/>

<createTable tableName="NameTable">
    <column name="id" type="${type.bigint}" autoIncrement="true">
        <constraints primaryKey="true" nullable="false" />
    </column>
    <!-- 仅MySQL生效,其他数据库忽略该属性 -->
    <column name="name" type="varchar(255)" ${mysql.name.collation:}>
        <constraints nullable="false" />
    </column>
</createTable>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:04:57