12 KiB
table-alias — 表结构对齐技能
版本: 1.1.1
作者: SQL Agent
创建时间: 2026-08-14
最后更新: 2026-08-14
重要注意事项
⚠️ \n 转义字符处理
问题描述:
MySQL SHOW CREATE TABLE 命令返回的建表语句中,字段定义之间使用字面量 \n 字符(两个字符:反斜杠 + n),而非真正的换行符。这会导致生成的 SQL 脚本格式异常,无法直接执行。
示例:
-- 错误格式(包含字面量 \n)
CREATE TABLE `test` (
\n `id` int NOT NULL,\n `name` varchar(50),\n PRIMARY KEY (`id`)
)
解决方案:
在脚本中获取 SHOW CREATE TABLE 输出后,必须经过三步处理:
# 1. cut -f2:提取第二列(SHOW CREATE TABLE 返回 表名+CREATE 语句,制表符分隔)
# 2. sed 's/\\n/\n/g':将字面量 \n 转换为真实换行符
# 3. sed '1d;$d':删除首尾行,提取表体部分
SOURCE_CREATE=$(mysql ... -e "SHOW CREATE TABLE ..." | cut -f2 | sed 's/\\n/\n/g')
TABLE_BODY=$(echo "$SOURCE_CREATE" | sed '1d;$d')
技术细节:
-
cut -f2-SHOW CREATE TABLE返回两列(表名、CREATE 语句),用制表符分隔,必须取第二列# 原始输出格式: table_name<TAB>CREATE TABLE `table_name` (\n `id` int...\n) # 错误:tail -1 仍取整行 # 正确:cut -f2 只取 CREATE 语句列 -
sed 's/\\n/\n/g'- 将字面量\n(两个字符)转换为真实换行符(一个字符)# 注意转义:sed 中 \\n 匹配字面量 \n,\n 替换为真实换行 -
sed '1d;$d'- 删除第一行(CREATE TABLE ... ()和最后一行() ENGINE=...),提取纯表体# 替代失效的正则提取(sed 默认不跨行匹配) # 1d = 删除第一行,$d = 删除最后一行
已生成脚本的批量修复:
如果脚本已生成但包含字面量 \n,使用以下命令批量修复:
cd /path/to/scripts
for f in *.sql; do sed -i 's/\\n/\n/g' "$f"; done
验证方法:
打开 SQL 文件,检查 CREATE TABLE 语句:
- ✅ 正确:字段定义分布在多行,格式清晰
- ❌ 错误:所有字段定义在一行,包含
\n字符
功能描述
将准生产数据库的表结构同步到VPS 开发库,确保开发环境与准生产环境表结构一致。
同步方向: 准生产(只读)→ VPS(root 可写)
支持场景:
- 新建表:目标库不存在该表时,执行
CREATE TABLE - 修改表:目标库已存在该表时,智能对比并执行
ALTER TABLE - 批量同步:支持同步整个数据库的所有表
触发方式
自然语言命令:
同步准生产 dmp_serp 的表结构到 VPS
同步准生产 dmp_smdm 的表结构到 VPS
参数解析:
- 源数据库:固定为"准生产"
- 源库名:从命令中提取(如
dmp_serp) - 目标数据库:固定为"VPS"
- 目标库名:默认与源库名相同
配置文件
位置: /home/yangxuan/.openclaw/workspace-sql/.db-sync-config.yaml
格式:
# 源数据库配置(准生产 - 只读)
sources:
- name: 准生产
host: 100.115.195.188
port: 50036
user: witsoftd
password_env: MYSQL_PWD_WIT
readonly: true
description: "准生产环境,只读权限"
# 目标数据库配置(VPS - root)
targets:
- name: VPS
host: 101.34.227.188
port: 3306
user: root
password_env: MYSQL_PWD_SQL
readonly: false
description: "VPS 开发环境,root 权限"
# 禁止同步的数据库黑名单
blacklist:
- ruoyi-vue-pro
- mysql
- sys
- performance_schema
# 输出配置
output:
base_dir: /home/yangxuan/codes/xuan-sql/scripts
date_subdir: true
write_memory: true
执行流程
1. 解析命令
从自然语言命令中提取:
- 源库名(如
dmp_serp) - 目标库名(默认与源库名相同)
2. 读取配置
从 .db-sync-config.yaml 读取:
- 源数据库连接信息(准生产)
- 目标数据库连接信息(VPS)
- 输出路径配置
3. 验证连接
# 验证源库连接(只读)
mysql -h <source_host> -P <source_port> -u <source_user> -p"${PASSWORD}" -e "SELECT 1;"
# 验证目标库连接(root)
mysql -h <target_host> -P <target_port> -u <target_user> -p"${PASSWORD}" -e "SELECT 1;"
4. 获取表列表
从源数据库获取所有表:
SHOW TABLES FROM <source_database>;
5. 对比并生成脚本
对每个表执行:
5.1 读取源表结构
SHOW CREATE TABLE <source_database>.<table_name>\G
5.2 读取目标表结构
-- 检查表是否存在
SHOW TABLES FROM <target_database> LIKE '<table_name>';
-- 如果存在,读取结构
SHOW CREATE TABLE <target_database>.<table_name>\G
5.3 智能对比
对比维度:
| 维度 | 对比方式 | 处理策略 |
|---|---|---|
| 表是否存在 | SHOW TABLES |
不存在 → CREATE TABLE |
| 字段列表 | 对比 information_schema.COLUMNS |
缺失 → ADD COLUMN |
| 字段类型 | 对比 COLUMN_TYPE |
不同 → MODIFY COLUMN |
| NULL 约束 | 对比 IS_NULLABLE |
不同 → MODIFY COLUMN |
| 默认值 | 对比 COLUMN_DEFAULT |
不同 → MODIFY COLUMN |
| 注释 | 对比 COLUMN_COMMENT |
不同 → MODIFY COLUMN |
| 索引 | 对比 information_schema.STATISTICS |
差异 → DROP/ADD INDEX |
| 主键 | 对比 COLUMN_KEY |
差异 → DROP/ADD PRIMARY KEY |
对比算法:
# 伪代码
def compare_tables(source_ddl, target_ddl):
changes = []
# 1. 目标表不存在 → CREATE
if target_ddl is None:
return ['CREATE', source_ddl]
# 2. 解析字段列表
source_fields = parse_fields(source_ddl)
target_fields = parse_fields(target_ddl)
# 3. 对比字段
for field in source_fields:
if field.name not in target_fields:
changes.append(['ADD COLUMN', field])
elif field != target_fields[field.name]:
changes.append(['MODIFY COLUMN', field])
# 4. 删除多余字段(目标有,源没有)
for field_name in target_fields:
if field_name not in source_fields:
changes.append(['DROP COLUMN', field_name])
# 5. 对比索引(略)
return changes
5.4 生成 SQL 脚本
单表脚本格式:
-- ============================================================
-- 表结构同步脚本:{table_name}
-- 源:准生产 ({source_host}:{source_port})
-- 目标:VPS ({target_host}:{target_port}) {target_database}
-- 生成时间:{timestamp}
-- ============================================================
USE {target_database};
-- 如果表不存在,创建表
CREATE TABLE IF NOT EXISTS {table_name} (
...
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 如果表已存在,修改结构
-- 添加缺失字段
ALTER TABLE {table_name} ADD COLUMN {field_name} {type} ...;
-- 修改字段类型
ALTER TABLE {table_name} MODIFY COLUMN {field_name} {type} ...;
-- 删除多余字段
ALTER TABLE {table_name} DROP COLUMN {field_name};
-- 添加索引
ALTER TABLE {table_name} ADD INDEX {index_name} ({columns});
-- ============================================================
-- 验证
-- ============================================================
-- SHOW CREATE TABLE {table_name}\G
6. 执行脚本
对每个生成的脚本:
# 执行单表脚本
mysql -h <target_host> -u <target_user> -p"${PASSWORD}" -D <target_database> < <script_file>
# 捕获错误,继续执行下一个表
if [ $? -ne 0 ]; then
echo "ERROR: Failed to sync ${table_name}"
log_error "${table_name}: ${error_message}"
else
echo "OK: Synced ${table_name}"
log_success "${table_name}"
fi
7. 记录日志
写入 memory/{date}.md:
## 表结构同步 - {timestamp}
**源:** 准生产 `{source_database}`
**目标:** VPS `{target_database}`
**处理表数:** {count}
**成功:** {success_count}
**失败:** {fail_count}
### 处理的表
| 表名 | 操作 | 状态 | 说明 |
|------|------|------|------|
| table1 | CREATE | ✅ | 新建表 |
| table2 | ALTER | ✅ | 添加 3 个字段 |
| table3 | ALTER | ❌ | 字段类型冲突 |
### 脚本位置
{output_dir}/{date}/ ├── README.md ├── {database}__{table1}struct.sql ├── {database}{table2}__struct.sql └── ...
错误处理
策略: 遇到错误时,记录日志并继续处理下一个表。
常见错误及处理:
| 错误 | 原因 | 处理 |
|---|---|---|
ERROR 1138: Invalid use of NULL value |
字段有 NULL 值,无法改为 NOT NULL | 先更新 NULL 值为默认值,再修改 |
ERROR 1060: Duplicate column name |
字段已存在 | 跳过,记录警告 |
ERROR 1054: Unknown column |
字段不存在 | 跳过,记录错误 |
ERROR 1171: All parts of a PRIMARY KEY must be NOT NULL |
主键字段为 NULL | 记录错误,需人工处理 |
| 连接超时 | 网络问题 | 重试 3 次,失败则跳过 |
安全限制
1. 源数据库限制
- 固定为"准生产"(
100.115.195.188:50036) - 只读权限,无法执行 DDL
2. 目标数据库限制
- 固定为"VPS"(
101.34.227.188:3306) - 黑名单数据库禁止同步:
ruoyi-vue-promysqlsysperformance_schema
3. 表数量限制
- 单次同步表数量 > 50 时,需用户确认
输出产物
1. SQL 脚本文件
路径: /home/yangxuan/codes/xuan-sql/scripts/{YYYYMMDD}/
命名: {database}__{table_name}__struct.sql
2. README.md
# 表结构同步脚本 - {date}
**源:** 准生产 `{source_host}:{source_port}` `{source_database}`
**目标:** VPS `{target_host}:{target_port}` `{target_database}`
**生成时间:** {timestamp}
**执行状态:** ✅ 已完成 / ⚠️ 部分失败
## 脚本清单
| 序号 | 脚本文件 | 操作类型 | 状态 |
|------|---------|---------|------|
| 1 | `{database}__{table1}__struct.sql` | CREATE TABLE | ✅ |
| 2 | `{database}__{table2}__struct.sql` | ALTER TABLE | ✅ |
| ... | ... | ... | ... |
## 执行结果
- 总表数:{total}
- 成功:{success}
- 失败:{fail}
## 验证命令
```sql
SHOW TABLES LIKE '{database_pattern}';
SHOW CREATE TABLE {table_name}\G
### 3. 记忆文件
**路径:** `memory/{date}.md`
**内容:** 执行摘要、表清单、脚本位置
---
## 示例
### 示例 1:同步 dmp_serp
**命令:**
同步准生产 dmp_serp 的表结构到 VPS
**执行:**
1. 读取配置:准生产 → VPS
2. 获取 `dmp_serp` 所有表(如 50 张)
3. 对每个表:
- 对比结构差异
- 生成 SQL 脚本
- 执行脚本
4. 写入 `memory/2026-08-14.md`
**输出:**
/home/yangxuan/codes/xuan-sql/scripts/20260814/ ├── README.md ├── dmp_serp__dmp_om_productinfo__struct.sql ├── dmp_serp__dmp_om_productinfo_cost__struct.sql └── ...
### 示例 2:同步 dmp_smdm
**命令:**
同步准生产 dmp_smdm 的表结构到 VPS
**执行:** 同上,目标库为 `dmp_smdm`
---
## 相关文件
- **技能目录:** `/home/yangxuan/.openclaw/workspace-sql/skills/table-alias/`
- **配置文件:** `/home/yangxuan/.openclaw/workspace-sql/.db-sync-config.yaml`
- **脚本输出:** `/home/yangxuan/codes/xuan-sql/scripts/`
- **记忆文件:** `/home/yangxuan/.openclaw/workspace-sql/memory/`
---
## 版本历史
| 版本 | 日期 | 变更 |
|------|------|------|
| 1.1.2 | 2026-08-14 | 补充 `\n` 转义字符处理的技术细节:`cut -f2` 提取第二列、`sed '1d;$d'` 提取表体 |
| 1.1.1 | 2026-08-14 | 修复 `\n` 转义字符问题:在 `SHOW CREATE TABLE` 输出处理中添加 `sed 's/\\n/\n/g'` 转换 |
| 1.1.0 | 2026-08-14 | 支持单表模式,避免全量同步的耗时和风险 |
| 1.0.0 | 2026-08-14 | 初始版本 |