# table-alias — 表结构对齐技能 **版本:** 1.1.1 **作者:** SQL Agent **创建时间:** 2026-08-14 **最后更新:** 2026-08-14 --- ## 重要注意事项 ### ⚠️ `\n` 转义字符处理 **问题描述:** MySQL `SHOW CREATE TABLE` 命令返回的建表语句中,字段定义之间使用**字面量** `\n` 字符(两个字符:反斜杠 + n),而非真正的换行符。这会导致生成的 SQL 脚本格式异常,无法直接执行。 **示例:** ```sql -- 错误格式(包含字面量 \n) CREATE TABLE `test` ( \n `id` int NOT NULL,\n `name` varchar(50),\n PRIMARY KEY (`id`) ) ``` **解决方案:** 在脚本中获取 `SHOW CREATE TABLE` 输出后,必须经过三步处理: ```bash # 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') ``` **技术细节:** 1. **`cut -f2`** - `SHOW CREATE TABLE` 返回两列(表名、CREATE 语句),用制表符分隔,必须取第二列 ``` # 原始输出格式: table_nameCREATE TABLE `table_name` (\n `id` int...\n) # 错误:tail -1 仍取整行 # 正确:cut -f2 只取 CREATE 语句列 ``` 2. **`sed 's/\\n/\n/g'`** - 将字面量 `\n`(两个字符)转换为真实换行符(一个字符) ```bash # 注意转义:sed 中 \\n 匹配字面量 \n,\n 替换为真实换行 ``` 3. **`sed '1d;$d'`** - 删除第一行(`CREATE TABLE ... (`)和最后一行(`) ENGINE=...`),提取纯表体 ```bash # 替代失效的正则提取(sed 默认不跨行匹配) # 1d = 删除第一行,$d = 删除最后一行 ``` **已生成脚本的批量修复:** 如果脚本已生成但包含字面量 `\n`,使用以下命令批量修复: ```bash 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` **格式:** ```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. 验证连接 ```bash # 验证源库连接(只读) mysql -h -P -u -p"${PASSWORD}" -e "SELECT 1;" # 验证目标库连接(root) mysql -h -P -u -p"${PASSWORD}" -e "SELECT 1;" ``` ### 4. 获取表列表 从源数据库获取所有表: ```sql SHOW TABLES FROM ; ``` ### 5. 对比并生成脚本 对每个表执行: #### 5.1 读取源表结构 ```sql SHOW CREATE TABLE .\G ``` #### 5.2 读取目标表结构 ```sql -- 检查表是否存在 SHOW TABLES FROM LIKE ''; -- 如果存在,读取结构 SHOW CREATE TABLE .\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` | **对比算法:** ```python # 伪代码 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 脚本 **单表脚本格式:** ```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. 执行脚本 对每个生成的脚本: ```bash # 执行单表脚本 mysql -h -u -p"${PASSWORD}" -D < # 捕获错误,继续执行下一个表 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`: ```markdown ## 表结构同步 - {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-pro` - `mysql` - `sys` - `performance_schema` ### 3. 表数量限制 - 单次同步表数量 > 50 时,需用户确认 --- ## 输出产物 ### 1. SQL 脚本文件 **路径:** `/home/yangxuan/codes/xuan-sql/scripts/{YYYYMMDD}/` **命名:** `{database}__{table_name}__struct.sql` ### 2. README.md ```markdown # 表结构同步脚本 - {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 | 初始版本 |