PostgreSQL下Liquibase实现跨表关联更新的非原生SQL替代方案
问题背景
- 项目基于Java Spring Boot开发,使用依赖版本:
org.liquibase.liquibase-core:3.8.9org.postgresql:postgresql:42.2.19
- 因历史设计过时,需要重构两张存在关联关系的表简化结构。
原表结构规则
clients表:初始以UUID类型的id作为主键,每条客户端记录带全局唯一的code字段passwords表:与clients表为一对多关系,表中client字段为关联clients.id的外键,单个客户端可对应多条密码记录- 存量数据示例:
clients表中id=a4ffc8be-13a4-4e4d-b243-00dc373a591c对应code=CODE_A,id=de34a0da-9bca-4b1f-a3bc-5d853fc52268对应code=CODE_Bpasswords表中id=9c1c9d57-ca3e-4990-9461-5e6cca3d1acc关联的client值为第一个UUID,id=8c7bfb7e-2ff0-4fc1-b0af-804d803c611b关联的client值为第二个UUID
重构目标
- 将
clients表主键替换为code字段,删除原有id列 - 将
passwords表的外键关联目标从clients.id改为clients.code - 同步更新
passwords.client字段的存量值,将原存储的UUID替换为对应客户端的code值,最终该字段存储CODE_A、CODE_B类业务编码
实现方案
你当前使用的Liquibase 3.8.9版本原生支持跨表关联更新,不需要插入原生<sql>代码块,用标准<update>标签即可实现。
核心逻辑是通过valueComputed属性传入关联子查询完成赋值,不需要依赖PostgreSQL特有的UPDATE...FROM语法,符合Liquibase脚本规范,具体XML写法如下:
<changeSet id="refactor_clients_passwords_fk_20240xxx" author="dev"> <!-- 1. 先调整passwords.client字段类型,和clients.code类型保持一致,原字段为UUID类型时必须执行此步 --> <modifyDataType tableName="passwords" columnName="client" newDataType="VARCHAR(64)"/> <!-- 长度和clients.code定义保持一致 --> <!-- 2. 原生update标签跨表更新存量数据 --> <update tableName="passwords"> <column name="client" valueComputed="(SELECT c.code FROM clients c WHERE c.id = passwords.client)"/> <where>passwords.client IS NOT NULL</where> </update> <!-- 后续按顺序执行以下步骤即可完成全量重构: 3. 删除passwords表原关联clients.id的外键约束 4. 删除clients表原主键约束,删除clients.id字段,将code设为新主键 5. 为passwords表添加关联clients.code的新外键约束 --> </changeSet>
注意:如果你的changelog使用YAML或JSON格式编写,逻辑完全一致,只需要将标签结构对应转换为YAML/JSON格式,核心保留
valueComputed的子查询赋值逻辑即可。
这种写法是Liquibase官方支持的标准更新方式,会被Liquibase的回滚机制、数据库方言适配逻辑正常识别,比直接写原生SQL更符合规范。
内容的提问来源于stack exchange,提问作者Iñaki Guastalli
相关产品推荐
相关产品推荐

