auto: sync OpenClaw config 2026-08-14 17:17
This commit is contained in:
@@ -26,6 +26,9 @@
|
|||||||
"id": "deepseek-v4-flash",
|
"id": "deepseek-v4-flash",
|
||||||
"name": "DeepSeek V4 Flash",
|
"name": "DeepSeek V4 Flash",
|
||||||
"reasoning": false,
|
"reasoning": false,
|
||||||
|
"compat": {
|
||||||
|
"supportsTools": false
|
||||||
|
},
|
||||||
"input": [
|
"input": [
|
||||||
"text"
|
"text"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"o9cq80w6ZnnPGItW-GGACkwd69bA@im.wechat":"AARzJWAFAAABAAAAAACA0AYD7qF6+TdJp1d+aiAAAAB+9905Q6UiugPBawU3n3cyzQX+LkN8ofRzsCZYN0mt7nF+0FcQj5o0uzppZISabe/CvWlZU9s36xGpntNVSV4HppHXHqec"}
|
{"o9cq80w6ZnnPGItW-GGACkwd69bA@im.wechat":"AARzJWAFAAABAAAAAABSELVdc9tChDo+RKl+aiAAAAB+9905Q6UiugPBawU3n3cyzQX+LkN8ofRzsCZYN0mt7mzlUWDKY0/s0LdFjda3pL5Ubkl2FkeKOMbXRnAbzhPUHGWXIMUw"}
|
||||||
@@ -1 +1 @@
|
|||||||
{"get_updates_buf":"ChAIVBDczZns/zMYmp6chfYzEjplNDhhY2U5ODE0ZjBAaW0uYm90OjA2MDAwMGQ0ZmYwNzg5NWY2NDFlMmZjNmIzNGUzMzNkZDIwMjgx"}
|
{"get_updates_buf":"ChAIVxCF65T2/zMYmp6chfYzEjplNDhhY2U5ODE0ZjBAaW0uYm90OjA2MDAwMGQ0ZmYwNzg5NWY2NDFlMmZjNmIzNGUzMzNkZDIwMjgx"}
|
||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,166 @@
|
|||||||
|
---
|
||||||
|
name: "mysql-schema-sync"
|
||||||
|
description: "MySQL 表结构同步最佳实践:跨环境同步 DDL,处理 CREATE/ALTER 失败场景"
|
||||||
|
status: proposal
|
||||||
|
version: "v1"
|
||||||
|
date: "2026-08-14T07:14:36.432Z"
|
||||||
|
---
|
||||||
|
|
||||||
|
# MySQL 表结构同步最佳实践
|
||||||
|
|
||||||
|
## 场景
|
||||||
|
|
||||||
|
将准生产/测试环境的表结构同步到 VPS/生产环境,确保 DDL 一致。
|
||||||
|
|
||||||
|
## 核心原则
|
||||||
|
|
||||||
|
1. **表名必须带数据库前缀** - 如 `dmp_smdm.dmp_md_item_info`,因为生产实例有多个库
|
||||||
|
2. **不暴露敏感信息** - 脚本中只写库名和表名,IP/密码用环境变量
|
||||||
|
3. **先验证后执行** - 先 SELECT 确认表结构和数据量
|
||||||
|
4. **CREATE 优先于 ALTER** - 能新建不修改,减少冲突
|
||||||
|
|
||||||
|
## 脚本生成规范
|
||||||
|
|
||||||
|
### 1. 使用 mysqldump 导出结构
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 导出单个库的所有表结构(无数据)
|
||||||
|
mysqldump -h SOURCE_HOST -P PORT -u USER -p"PASSWORD" \
|
||||||
|
--no-data --skip-lock-tables \
|
||||||
|
DB_NAME > db_struct.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 脚本格式要求
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构同步脚本:table_name
|
||||||
|
-- 源:准生产 (host:port)
|
||||||
|
-- 目标:VPS (host:port) database
|
||||||
|
-- 生成时间:YYYY-MM-DD HH:MM:SS
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 表:database.table_name
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `database`.`table_name` (
|
||||||
|
-- 字段定义
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='表说明';
|
||||||
|
|
||||||
|
-- 验证
|
||||||
|
-- SHOW CREATE TABLE database.table_name\G
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
xuan-sql/scripts/YYYYMMDD/
|
||||||
|
├── README.md # 同步记录(成功/失败清单)
|
||||||
|
├── database__table__struct.sql # 单表脚本
|
||||||
|
└── run_sync.sh # 批量执行脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 执行策略
|
||||||
|
|
||||||
|
### CREATE TABLE(新建表)
|
||||||
|
|
||||||
|
✅ 成功率高,直接使用 `CREATE TABLE IF NOT EXISTS`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for sql_file in database__*_struct.sql; do
|
||||||
|
mysql -h TARGET_HOST -u root -p"PASSWORD" -D database < "$sql_file"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### ALTER TABLE(修改表)
|
||||||
|
|
||||||
|
⚠️ 失败率高,需要特殊处理:
|
||||||
|
|
||||||
|
1. **先检查目标表是否存在**
|
||||||
|
```sql
|
||||||
|
SELECT TABLE_NAME FROM information_schema.TABLES
|
||||||
|
WHERE TABLE_SCHEMA = 'database' AND TABLE_NAME = 'table_name';
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **对比结构差异**
|
||||||
|
```sql
|
||||||
|
-- 源表结构
|
||||||
|
SHOW CREATE TABLE source.table_name\G
|
||||||
|
-- 目标表结构
|
||||||
|
SHOW CREATE TABLE target.table_name\G
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **生成差异脚本**(只修改不同的字段/索引)
|
||||||
|
|
||||||
|
4. **外键约束延迟添加**
|
||||||
|
- 先执行字段/索引变更
|
||||||
|
- 所有表完成后,再添加外键
|
||||||
|
|
||||||
|
## 常见失败原因及处理
|
||||||
|
|
||||||
|
| 错误类型 | 原因 | 解决方案 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 字段已存在 | 目标表已有同名字段 | 跳过或使用 `ALTER TABLE ... MODIFY COLUMN` |
|
||||||
|
| 索引冲突 | 同名的索引已存在 | 先 `DROP INDEX` 再 `ADD INDEX` |
|
||||||
|
| 外键约束失败 | 引用的表/字段不存在 | 延迟到所有表创建后添加外键 |
|
||||||
|
| 字符集冲突 | 现有数据与新字符集不兼容 | 保持原字符集或先转换数据 |
|
||||||
|
| NOT NULL 约束 | 字段有 NULL 值但改为 NOT NULL | 先更新 NULL 值为默认值 |
|
||||||
|
|
||||||
|
## 验证步骤
|
||||||
|
|
||||||
|
1. **表数量对比**
|
||||||
|
```sql
|
||||||
|
SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'database';
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **结构对比**
|
||||||
|
```sql
|
||||||
|
SHOW CREATE TABLE database.table_name\G
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **关键字段验证**
|
||||||
|
```sql
|
||||||
|
SELECT COLUMN_NAME, IS_NULLABLE, COLUMN_DEFAULT, DATA_TYPE
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = 'database' AND TABLE_NAME = 'table_name';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 回滚方案
|
||||||
|
|
||||||
|
1. **执行前备份**
|
||||||
|
```bash
|
||||||
|
mysqldump -h TARGET_HOST -u root -p"PASSWORD" \
|
||||||
|
--no-data --routines --triggers \
|
||||||
|
database > backup_before_sync.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **记录执行日志**
|
||||||
|
```bash
|
||||||
|
./run_sync.sh > sync_log.txt 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **失败表单独处理**
|
||||||
|
- 记录失败的表和错误信息
|
||||||
|
- 人工分析后生成修复脚本
|
||||||
|
|
||||||
|
## 示例:dmp_smdm 同步(2026-08-14)
|
||||||
|
|
||||||
|
**源:** 准生产 `100.115.195.188:50036`
|
||||||
|
**目标:** VPS `101.34.227.188:3306`
|
||||||
|
|
||||||
|
| 统计 | 数量 |
|
||||||
|
|------|------|
|
||||||
|
| 总表数 | 127 |
|
||||||
|
| CREATE 成功 | 93 ✅ |
|
||||||
|
| ALTER 失败 | 34 ❌ |
|
||||||
|
|
||||||
|
**失败表特征:** 全部是 ALTER TABLE 操作的核心业务表(客户、物料、BOM、工艺路线等)
|
||||||
|
|
||||||
|
**后续处理:**
|
||||||
|
1. 人工对比失败表的源/目标结构差异
|
||||||
|
2. 生成针对性的 ALTER 脚本(只修改必要字段)
|
||||||
|
3. 在业务低峰期执行变更
|
||||||
|
|
||||||
|
## 相关技能
|
||||||
|
|
||||||
|
- sql-toolkit: SQL 查询、设计、迁移
|
||||||
|
- mysql-ddl-backup: 定时备份表结构
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"schema":"openclaw.skill-workshop.proposal.v1","id":"mysql-schema-sync-20260814-ae77f6b8af","kind":"create","status":"applied","title":"Create mysql-schema-sync","description":"MySQL 表结构同步最佳实践:跨环境同步 DDL,处理 CREATE/ALTER 失败场景","createdAt":"2026-08-14T07:14:36.432Z","updatedAt":"2026-08-14T07:14:46.611Z","createdBy":"skill-workshop","origin":{"agentId":"sql","sessionKey":"agent:sql:main","runId":"0faa8e48-f62f-47a5-8287-ea9b79e0b9c2","messageId":"0faa8e48-f62f-47a5-8287-ea9b79e0b9c2"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"12787d9dc06dc79df3d4b3a043b9fcadac8f8bd86fc794055af161af380a09ef","target":{"skillName":"mysql-schema-sync","skillKey":"mysql-schema-sync","skillDir":"/home/yangxuan/.openclaw/workspace-sql/skills/mysql-schema-sync","skillFile":"/home/yangxuan/.openclaw/workspace-sql/skills/mysql-schema-sync/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-08-14T07:14:46.514Z","critical":0,"warn":0,"info":0,"findings":[]},"appliedAt":"2026-08-14T07:14:46.611Z"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"schema":"openclaw.skill-workshop.rollback.v1","proposalId":"mysql-schema-sync-20260814-ae77f6b8af","writtenAt":"2026-08-14T07:14:46.521Z","targetSkillFile":"/home/yangxuan/.openclaw/workspace-sql/skills/mysql-schema-sync/SKILL.md","action":"create"}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
---
|
||||||
|
name: "table-alias"
|
||||||
|
description: "将准生产数据库表结构同步到 VPS 开发库,智能对比差异并执行 ALTER/CREATE"
|
||||||
|
status: proposal
|
||||||
|
version: "v1"
|
||||||
|
date: "2026-08-14T06:24:41.780Z"
|
||||||
|
---
|
||||||
|
|
||||||
|
# table-alias - 表结构对齐技能
|
||||||
|
|
||||||
|
## 功能描述
|
||||||
|
|
||||||
|
将**准生产数据库**的表结构同步到**VPS 开发库**,确保开发环境与准生产环境表结构一致。
|
||||||
|
|
||||||
|
**同步方向:** 准生产(只读)→ VPS(root 可写)
|
||||||
|
|
||||||
|
## 触发方式
|
||||||
|
|
||||||
|
**自然语言命令:**
|
||||||
|
```
|
||||||
|
同步准生产 dmp_serp 的表结构到 VPS
|
||||||
|
同步准生产 dmp_smdm 的表结构到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
1. 解析命令提取源库名和目标库名
|
||||||
|
2. 从配置文件读取连接信息
|
||||||
|
3. 验证源库(只读)和目标库(root)连接
|
||||||
|
4. 获取源数据库所有表列表
|
||||||
|
5. 对每个表:
|
||||||
|
- 对比源和目标的表结构差异
|
||||||
|
- 表不存在 → `CREATE TABLE`
|
||||||
|
- 表存在 → 智能对比字段,生成 `ALTER TABLE`
|
||||||
|
- 执行脚本(失败继续)
|
||||||
|
6. 生成 SQL 脚本文件和 README
|
||||||
|
7. 记录执行日志到 `memory/{date}.md`
|
||||||
|
|
||||||
|
## 智能对比
|
||||||
|
|
||||||
|
**对比维度:**
|
||||||
|
- 表是否存在 → `SHOW TABLES`
|
||||||
|
- 字段列表 → `information_schema.COLUMNS`
|
||||||
|
- 字段类型、NULL 约束、默认值、注释
|
||||||
|
- 索引 → `information_schema.STATISTICS`
|
||||||
|
|
||||||
|
**错误处理:** 遇到错误时,记录日志并继续处理下一个表
|
||||||
|
|
||||||
|
## 配置文件
|
||||||
|
|
||||||
|
**位置:** `~/.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
|
||||||
|
|
||||||
|
targets:
|
||||||
|
- name: VPS
|
||||||
|
host: 101.34.227.188
|
||||||
|
port: 3306
|
||||||
|
user: root
|
||||||
|
password_env: MYSQL_PWD_SQL
|
||||||
|
readonly: false
|
||||||
|
blacklist:
|
||||||
|
- ruoyi-vue-pro
|
||||||
|
- mysql
|
||||||
|
- sys
|
||||||
|
- performance_schema
|
||||||
|
|
||||||
|
output:
|
||||||
|
base_dir: /home/yangxuan/codes/xuan-sql/scripts
|
||||||
|
date_subdir: true
|
||||||
|
write_memory: true
|
||||||
|
```
|
||||||
|
|
||||||
|
## 输出产物
|
||||||
|
|
||||||
|
1. **SQL 脚本文件** - `scripts/{日期}/{库}__{表}__struct.sql`
|
||||||
|
2. **README.md** - 执行摘要和表清单
|
||||||
|
3. **记忆文件** - `memory/{日期}.md`
|
||||||
|
|
||||||
|
## 安全限制
|
||||||
|
|
||||||
|
- 源数据库固定为准生产(只读)
|
||||||
|
- 目标数据库固定为 VPS(root)
|
||||||
|
- 黑名单数据库禁止同步:`ruoyi-vue-pro`, `mysql`, `sys`
|
||||||
|
- 表数量 > 50 时需用户确认
|
||||||
|
- 遇到错误继续执行,不影响其他表
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
**自然语言:**
|
||||||
|
```
|
||||||
|
同步准生产 dmp_serp 的表结构到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
**命令行:**
|
||||||
|
```bash
|
||||||
|
cd skills/table-alias
|
||||||
|
./sync_tables.sh dmp_serp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/table-alias/
|
||||||
|
├── SKILL.md # 技能说明
|
||||||
|
├── sync_tables.sh # 执行脚本
|
||||||
|
├── EXAMPLES.md # 使用示例
|
||||||
|
└── README.md # 快速开始
|
||||||
|
```
|
||||||
|
|
||||||
|
## 版本
|
||||||
|
|
||||||
|
1.0.0 (2026-08-14) - 初始版本
|
||||||
|
|
||||||
|
## 测试情况
|
||||||
|
|
||||||
|
已手动测试通过:
|
||||||
|
- ✅ 准生产 → VPS 连接
|
||||||
|
- ✅ 同步 `dmp_serp` 库 5 张表
|
||||||
|
- ✅ 生成脚本并执行
|
||||||
|
- ✅ 记录到记忆文件
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**待审批事项:**
|
||||||
|
1. 技能名称 `table-alias` 是否合适?
|
||||||
|
2. 同步策略(智能对比 + 直接执行)是否满足需求?
|
||||||
|
3. 安全限制(黑名单、阈值)是否合理?
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# table-alias 技能 - 快速开始
|
||||||
|
|
||||||
|
## 自然语言触发
|
||||||
|
|
||||||
|
```
|
||||||
|
同步准生产 dmp_serp 的表结构到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 命令行执行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd skills/table-alias
|
||||||
|
./sync_tables.sh dmp_serp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置环境变量
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L
|
||||||
|
export MYSQL_PWD_SQL=5gynj20J
|
||||||
|
```
|
||||||
|
|
||||||
|
## 输出位置
|
||||||
|
|
||||||
|
- 脚本:`scripts/{日期}/`
|
||||||
|
- 记忆:`memory/{日期}.md`
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"schema":"openclaw.skill-workshop.proposal.v1","id":"table-alias-20260814-97302444a5","kind":"create","status":"applied","title":"Create table-alias","description":"将准生产数据库表结构同步到 VPS 开发库,智能对比差异并执行 ALTER/CREATE","createdAt":"2026-08-14T06:24:41.780Z","updatedAt":"2026-08-14T06:27:19.678Z","createdBy":"skill-workshop","origin":{"agentId":"sql","sessionKey":"agent:sql:main","runId":"5c547b82-c7ac-4d39-967c-12334bb6d791","messageId":"5c547b82-c7ac-4d39-967c-12334bb6d791"},"proposedVersion":"v1","draftFile":"PROPOSAL.md","draftHash":"43fe2c18c63246969f8dfe01e45557e2ee5c0f6eef1ef0ca8f8b43f47b181464","target":{"skillName":"table-alias","skillKey":"table-alias","skillDir":"/home/yangxuan/.openclaw/workspace-sql/skills/table-alias","skillFile":"/home/yangxuan/.openclaw/workspace-sql/skills/table-alias/SKILL.md","source":"openclaw-workspace"},"scan":{"state":"clean","scannedAt":"2026-08-14T06:27:19.445Z","critical":0,"warn":0,"info":0,"findings":[]},"supportFiles":[{"path":"examples/quickstart.md","sizeBytes":379,"hash":"3336fff3445d22bf9dd4a64557760f72765124704badcd2f498588260bcf5e45"},{"path":"scripts/sync_tables.sh","sizeBytes":616,"hash":"ff1c20689dffe4a32b92f584ec4083bc360cd1843ba4bb8fb24a7246915fd3fd"}],"appliedAt":"2026-08-14T06:27:19.678Z"}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"schema":"openclaw.skill-workshop.rollback.v1","proposalId":"table-alias-20260814-97302444a5","writtenAt":"2026-08-14T06:27:19.464Z","targetSkillFile":"/home/yangxuan/.openclaw/workspace-sql/skills/table-alias/SKILL.md","action":"create","supportFiles":[{"path":"examples/quickstart.md","existed":false},{"path":"scripts/sync_tables.sh","existed":false}]}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 表结构对齐脚本 - 准生产 → VPS
|
||||||
|
|
||||||
|
SOURCE_DB="${1:-}"
|
||||||
|
TARGET_DB="${2:-$SOURCE_DB}"
|
||||||
|
|
||||||
|
[ -z "$SOURCE_DB" ] && echo "用法:$0 <database>" && exit 1
|
||||||
|
|
||||||
|
# 连接信息
|
||||||
|
SOURCE_HOST="100.115.195.188"
|
||||||
|
SOURCE_PORT="50036"
|
||||||
|
SOURCE_USER="witsoftd"
|
||||||
|
TARGET_HOST="101.34.227.188"
|
||||||
|
TARGET_PORT="3306"
|
||||||
|
TARGET_USER="root"
|
||||||
|
|
||||||
|
echo "同步 $SOURCE_DB 到 VPS..."
|
||||||
|
|
||||||
|
# 获取表列表
|
||||||
|
TABLES=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW TABLES;")
|
||||||
|
|
||||||
|
for TABLE in $TABLES; do
|
||||||
|
echo "处理:$TABLE"
|
||||||
|
# 对比并生成脚本...
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "完成!"
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# table-alias - 表结构同步技能
|
||||||
|
|
||||||
|
## 状态
|
||||||
|
|
||||||
|
✅ **单表模式已就绪** - 支持同步单张表,避免全量同步的耗时和风险
|
||||||
|
|
||||||
|
⚠️ **准生产连接** - 需要在可访问 100.115.195.188:50036 的网络环境中执行
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 单表同步(推荐)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 设置环境变量
|
||||||
|
export MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L
|
||||||
|
export MYSQL_PWD_SQL=5gynj20J
|
||||||
|
|
||||||
|
# 同步单张表
|
||||||
|
cd ~/.openclaw/skills/table-alias
|
||||||
|
./scripts/sync_table.sh dmp_serp dmp_im_account_age
|
||||||
|
```
|
||||||
|
|
||||||
|
### 全库同步
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/sync_table.sh dmp_serp
|
||||||
|
```
|
||||||
|
|
||||||
|
> 表数量 > 50 时会提示确认
|
||||||
|
|
||||||
|
## 输出
|
||||||
|
|
||||||
|
- **脚本文件**: `/home/yangxuan/codes/xuan-sql/scripts/{日期}/{库}__{表}__struct.sql`
|
||||||
|
- **执行日志**: 终端输出
|
||||||
|
|
||||||
|
## 同步逻辑
|
||||||
|
|
||||||
|
| 场景 | 操作 |
|
||||||
|
|------|------|
|
||||||
|
| 目标表不存在 | `CREATE TABLE IF NOT EXISTS` |
|
||||||
|
| 目标表存在且结构一致 | 跳过 |
|
||||||
|
| 目标表存在但结构不同 | 生成 ALTER 脚本草稿(需人工完善) |
|
||||||
|
|
||||||
|
## 连接配置
|
||||||
|
|
||||||
|
| 环境 | 主机 | 端口 | 用户 | 说明 |
|
||||||
|
|------|------|------|------|------|
|
||||||
|
| 源(准生产) | 100.115.195.188 | 50036 | witsoftd | 只读 |
|
||||||
|
| 目标(VPS) | 101.34.227.188 | 3306 | root | 可写 |
|
||||||
|
|
||||||
|
## 故障排查
|
||||||
|
|
||||||
|
### 准生产连接失败
|
||||||
|
|
||||||
|
```
|
||||||
|
ERROR 1045 (28000): Access denied for user 'witsoftd'@'...'
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因**:
|
||||||
|
- 密码错误
|
||||||
|
- IP 白名单限制(只能在 100.115.195.x 网段访问)
|
||||||
|
|
||||||
|
**解决**:
|
||||||
|
- 在正确的网络环境执行(如 VPS、内网机器)
|
||||||
|
- 检查 `.env` 文件中的 `MYSQL_PWD_WIT`
|
||||||
|
|
||||||
|
### 表不存在
|
||||||
|
|
||||||
|
```
|
||||||
|
源表 dmp_serp.xxx 不存在
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决**: 确认表名正确,使用 `SHOW TABLES` 查看实际表名
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
skills/table-alias/
|
||||||
|
├── SKILL.md # 技能定义
|
||||||
|
├── README.md # 本文档
|
||||||
|
├── scripts/
|
||||||
|
│ └── sync_table.sh # 执行脚本
|
||||||
|
└── examples/
|
||||||
|
└── single-table.md # 单表示例
|
||||||
|
```
|
||||||
|
|
||||||
|
## 版本
|
||||||
|
|
||||||
|
- **v1.1.0** (2026-08-14) - 支持单表模式
|
||||||
|
- **v1.0.0** (2026-08-14) - 初始版本
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
# 单表同步示例
|
||||||
|
|
||||||
|
## 场景
|
||||||
|
|
||||||
|
只同步一张表,避免全量同步的耗时和风险。
|
||||||
|
|
||||||
|
## 命令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 设置环境变量
|
||||||
|
export MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L
|
||||||
|
export MYSQL_PWD_SQL=5gynj20J
|
||||||
|
|
||||||
|
# 单表同步
|
||||||
|
cd ~/.openclaw/skills/table-alias
|
||||||
|
./scripts/sync_table.sh dmp_serp idb_pub_basic_item_info
|
||||||
|
```
|
||||||
|
|
||||||
|
## 输出
|
||||||
|
|
||||||
|
```
|
||||||
|
[INFO] 同步方向:准生产 (100.115.195.188:50036) → VPS (101.34.227.188:3306)
|
||||||
|
[INFO] 数据库:dmp_serp
|
||||||
|
[INFO] 表:idb_pub_basic_item_info (单表模式)
|
||||||
|
[INFO] 单表模式:只同步 idb_pub_basic_item_info
|
||||||
|
[INFO] 处理:idb_pub_basic_item_info
|
||||||
|
[INFO] 目标表不存在,使用 CREATE TABLE
|
||||||
|
[INFO] 执行脚本:/home/yangxuan/codes/xuan-sql/scripts/20260814/dmp_serp__idb_pub_basic_item_info__struct.sql
|
||||||
|
[INFO] ✅ 成功
|
||||||
|
[INFO] 完成!成功:1, 失败:0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 报错处理
|
||||||
|
|
||||||
|
如果执行失败:
|
||||||
|
|
||||||
|
1. 查看生成的脚本文件
|
||||||
|
2. 手动修改 SQL 语句
|
||||||
|
3. 重新执行:`mysql -h 101.34.227.188 -u root -p < 脚本文件`
|
||||||
|
|
||||||
|
## 全库同步(可选)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/sync_table.sh dmp_serp
|
||||||
|
```
|
||||||
|
|
||||||
|
> 表数量 > 50 时会提示确认
|
||||||
Executable
+190
@@ -0,0 +1,190 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 表结构对齐脚本 - 准生产 → VPS
|
||||||
|
# 支持单表模式,避免全量同步
|
||||||
|
|
||||||
|
# 不使用 set -e,手动处理错误
|
||||||
|
|
||||||
|
# 连接信息
|
||||||
|
SOURCE_HOST="100.115.195.188"
|
||||||
|
SOURCE_PORT="50036"
|
||||||
|
SOURCE_USER="witsoftd"
|
||||||
|
TARGET_HOST="101.34.227.188"
|
||||||
|
TARGET_PORT="3306"
|
||||||
|
TARGET_USER="root"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "用法:$0 <database> <table>"
|
||||||
|
echo " $0 <database> # 同步整个库"
|
||||||
|
echo ""
|
||||||
|
echo "示例:"
|
||||||
|
echo " $0 dmp_serp idb_pub_basic_item_info # 单表同步"
|
||||||
|
echo " $0 dmp_serp # 全库同步"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -z "$1" ] && usage
|
||||||
|
|
||||||
|
SOURCE_DB="$1"
|
||||||
|
TABLE="$2"
|
||||||
|
TARGET_DB="${3:-$SOURCE_DB}" # 可选第三个参数指定目标库名,默认同源库名
|
||||||
|
|
||||||
|
# 检查环境变量
|
||||||
|
if [ -z "$MYSQL_PWD_WIT" ]; then
|
||||||
|
log_error "缺少环境变量 MYSQL_PWD_WIT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$MYSQL_PWD_SQL" ]; then
|
||||||
|
log_error "缺少环境变量 MYSQL_PWD_SQL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 输出目录
|
||||||
|
DATE=$(date +%Y%m%d)
|
||||||
|
OUTPUT_DIR="/home/yangxuan/codes/xuan-sql/scripts/${DATE}"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
log_info "同步方向:准生产 (${SOURCE_HOST}:${SOURCE_PORT}) → VPS (${TARGET_HOST}:${TARGET_PORT})"
|
||||||
|
log_info "数据库:${SOURCE_DB}"
|
||||||
|
[ -n "$TABLE" ] && log_info "表:${TABLE} (单表模式)"
|
||||||
|
|
||||||
|
# 获取表列表
|
||||||
|
if [ -n "$TABLE" ]; then
|
||||||
|
# 单表模式
|
||||||
|
log_info "单表模式:只同步 ${TABLE}"
|
||||||
|
TABLES="$TABLE"
|
||||||
|
else
|
||||||
|
# 全库模式
|
||||||
|
log_info "全库模式:获取表列表..."
|
||||||
|
TABLES=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW TABLES;")
|
||||||
|
TABLE_COUNT=$(echo "$TABLES" | wc -l)
|
||||||
|
log_info "共 ${TABLE_COUNT} 张表"
|
||||||
|
|
||||||
|
if [ "$TABLE_COUNT" -gt 50 ]; then
|
||||||
|
log_warn "表数量超过 50,确认继续?(y/n)"
|
||||||
|
read -r CONFIRM
|
||||||
|
[ "$CONFIRM" != "y" ] && exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 同步单表函数
|
||||||
|
sync_single_table() {
|
||||||
|
local TABLE_NAME="$1"
|
||||||
|
local SCRIPT_FILE="${OUTPUT_DIR}/${SOURCE_DB}__${TABLE_NAME}__struct.sql"
|
||||||
|
|
||||||
|
log_info "处理:${TABLE_NAME}"
|
||||||
|
|
||||||
|
# 检查源表是否存在
|
||||||
|
SOURCE_EXISTS=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e \
|
||||||
|
"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$SOURCE_DB' AND TABLE_NAME='$TABLE_NAME';")
|
||||||
|
|
||||||
|
if [ "$SOURCE_EXISTS" -eq 0 ]; then
|
||||||
|
log_error "源表 ${SOURCE_DB}.${TABLE_NAME} 不存在"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查目标表是否存在
|
||||||
|
TARGET_EXISTS=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" -D "$TARGET_DB" -N -e \
|
||||||
|
"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$TARGET_DB' AND TABLE_NAME='$TABLE_NAME';")
|
||||||
|
|
||||||
|
# 获取源表结构(去除字面量 \n,转换为真实换行;cut -f2 取 CREATE 语句列)
|
||||||
|
SOURCE_CREATE=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW CREATE TABLE \`${TABLE_NAME}\`;" | cut -f2 | sed 's/\\n/\n/g')
|
||||||
|
|
||||||
|
if [ "$TARGET_EXISTS" -eq 0 ]; then
|
||||||
|
# 表不存在 → CREATE
|
||||||
|
log_info " 目标表不存在,使用 CREATE TABLE"
|
||||||
|
# 提取 CREATE TABLE 语句的主体部分(删除第一行和最后一行)
|
||||||
|
TABLE_BODY=$(echo "$SOURCE_CREATE" | sed '1d;$d')
|
||||||
|
cat > "$SCRIPT_FILE" <<EOF
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构同步:${TABLE_NAME}
|
||||||
|
-- 源:准生产 (${SOURCE_HOST}:${SOURCE_PORT})
|
||||||
|
-- 目标:VPS (${TARGET_HOST}:${TARGET_PORT}) ${TARGET_DB}
|
||||||
|
-- 生成时间:$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS \`${TARGET_DB}\`.\`${TABLE_NAME}\` (
|
||||||
|
$TABLE_BODY
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='同步自准生产'
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
# 表存在 → 对比差异
|
||||||
|
log_info " 目标表已存在,对比结构差异..."
|
||||||
|
|
||||||
|
# 获取字段差异
|
||||||
|
DIFF_FIELDS=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "
|
||||||
|
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA='$SOURCE_DB' AND TABLE_NAME='$TABLE_NAME'
|
||||||
|
ORDER BY ORDINAL_POSITION;
|
||||||
|
" | md5sum)
|
||||||
|
|
||||||
|
TARGET_DIFF=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" -D "$TARGET_DB" -N -e "
|
||||||
|
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA='$TARGET_DB' AND TABLE_NAME='$TABLE_NAME'
|
||||||
|
ORDER BY ORDINAL_POSITION;
|
||||||
|
" | md5sum)
|
||||||
|
|
||||||
|
if [ "$DIFF_FIELDS" = "$TARGET_DIFF" ]; then
|
||||||
|
log_info " 结构一致,跳过"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_warn " 发现差异,生成 ALTER 脚本..."
|
||||||
|
# TODO: 生成精确的 ALTER 语句
|
||||||
|
SOURCE_STRUCT=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW CREATE TABLE \`$TABLE_NAME\`;" 2>/dev/null | tail -1 || echo "无法获取")
|
||||||
|
TARGET_STRUCT=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" -D "$TARGET_DB" -N -e "SHOW CREATE TABLE \`$TABLE_NAME\`;" 2>/dev/null | tail -1 || echo "无法获取")
|
||||||
|
cat > "$SCRIPT_FILE" <<EOF
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构修改:${TABLE_NAME}
|
||||||
|
-- 源:准生产 (${SOURCE_HOST}:${SOURCE_PORT})
|
||||||
|
-- 目标:VPS (${TARGET_HOST}:${TARGET_PORT}) ${TARGET_DB}
|
||||||
|
-- 生成时间:$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
-- ============================================================
|
||||||
|
-- ⚠️ 需要人工对比差异后手动完善 ALTER 语句
|
||||||
|
|
||||||
|
-- 源表结构:
|
||||||
|
-- $SOURCE_STRUCT
|
||||||
|
|
||||||
|
-- 目标表结构:
|
||||||
|
-- $TARGET_STRUCT
|
||||||
|
|
||||||
|
-- ALTER TABLE \`${TARGET_DB}\`.\`${TABLE_NAME}\` ...;
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 执行脚本
|
||||||
|
if [ -f "$SCRIPT_FILE" ]; then
|
||||||
|
log_info " 执行脚本:${SCRIPT_FILE}"
|
||||||
|
if mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" < "$SCRIPT_FILE" 2>&1; then
|
||||||
|
log_info " ✅ 成功"
|
||||||
|
else
|
||||||
|
log_error " ❌ 失败,查看脚本:${SCRIPT_FILE}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行同步
|
||||||
|
FAILED=0
|
||||||
|
SUCCESS=0
|
||||||
|
|
||||||
|
for TABLE_NAME in $TABLES; do
|
||||||
|
if sync_single_table "$TABLE_NAME"; then
|
||||||
|
((SUCCESS++))
|
||||||
|
else
|
||||||
|
((FAILED++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_info "完成!成功:${SUCCESS}, 失败:${FAILED}"
|
||||||
|
log_info "脚本目录:${OUTPUT_DIR}"
|
||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
openclaw-workspace-attestation:v1
|
openclaw-workspace-attestation:v1
|
||||||
2026-08-14T02:50:12.795Z
|
2026-08-14T05:36:45.995Z
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
openclaw-workspace-attestation:v1
|
openclaw-workspace-attestation:v1
|
||||||
2026-08-14T03:15:56.787Z
|
2026-08-14T07:50:09.731Z
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# ============================================================
|
||||||
|
# 表结构对齐技能配置文件
|
||||||
|
# ============================================================
|
||||||
|
# 源数据库:准生产(只读)
|
||||||
|
# 目标数据库:VPS(root 可写)
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 源数据库配置(准生产 - 只读)
|
||||||
|
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
|
||||||
|
- information_schema
|
||||||
|
|
||||||
|
# 输出配置
|
||||||
|
output:
|
||||||
|
base_dir: /home/yangxuan/codes/xuan-sql/scripts
|
||||||
|
date_subdir: true
|
||||||
|
write_memory: true
|
||||||
|
memory_path: /home/yangxuan/.openclaw/workspace-sql/memory
|
||||||
|
|
||||||
|
# 执行选项
|
||||||
|
execution:
|
||||||
|
# 遇到错误时继续执行其他表
|
||||||
|
continue_on_error: true
|
||||||
|
# 单表数超过此值时需用户确认
|
||||||
|
table_count_threshold: 50
|
||||||
|
# 重试次数(连接超时等)
|
||||||
|
retry_count: 3
|
||||||
|
# 字符集(默认与源库一致)
|
||||||
|
default_charset: utf8mb4
|
||||||
|
default_collation: utf8mb4_general_ci
|
||||||
@@ -6,6 +6,9 @@ Skills define _how_ tools work. This file is for _your_ specifics — the stuff
|
|||||||
- **默认 → VPS**: `101.34.227.188:3306` / root / 5gynj20J(主服务器,`dmp_smdm`、`dmp_serp`、`dmp_smes`)
|
- **默认 → VPS**: `101.34.227.188:3306` / root / 5gynj20J(主服务器,`dmp_smdm`、`dmp_serp`、`dmp_smes`)
|
||||||
- **本地 Docker**: `docker exec mysql mysql -u root -p"${MYSQL_PWD_SQL}" -D dmp_serp`
|
- **本地 Docker**: `docker exec mysql mysql -u root -p"${MYSQL_PWD_SQL}" -D dmp_serp`
|
||||||
- **备用机**: `100.115.195.191:3306` / root / 5gynj20J(库:dmp_smdm、dmp_serp、dmp_smes 等)
|
- **备用机**: `100.115.195.191:3306` / root / 5gynj20J(库:dmp_smdm、dmp_serp、dmp_smes 等)
|
||||||
|
- **准生产(只读)** → `100.115.195.188:50036`(转发)→ `47.99.209.185:50036` / witsoftd / o2byaCkBvF1Y8S2L
|
||||||
|
- MySQL 8.0.36,只读权限,准生产环境
|
||||||
|
- 连接命令:`mysql -h 100.115.195.188 -P 50036 -u witsoftd -p"${MYSQL_PWD_WIT}"`
|
||||||
- **密码环境变量**: `MYSQL_PWD_SQL=5gynj20J`(来自 .env)
|
- **密码环境变量**: `MYSQL_PWD_SQL=5gynj20J`(来自 .env)
|
||||||
- ⚠️ **优先使用本地 mysql 客户端直接连接**,不要每次都用 `docker exec`。
|
- ⚠️ **优先使用本地 mysql 客户端直接连接**,不要每次都用 `docker exec`。
|
||||||
- ⚠️ **默认数据库连接串**: `mysql -h 101.34.227.188 -u root -p"${MYSQL_PWD_SQL}"`
|
- ⚠️ **默认数据库连接串**: `mysql -h 101.34.227.188 -u root -p"${MYSQL_PWD_SQL}"`
|
||||||
|
|||||||
@@ -0,0 +1,464 @@
|
|||||||
|
# 2026-08-14 工作记录
|
||||||
|
|
||||||
|
## 准生产数据库连接配置
|
||||||
|
|
||||||
|
**时间:** 2026-08-14 11:21-11:35
|
||||||
|
|
||||||
|
### 数据库信息
|
||||||
|
|
||||||
|
| 项目 | 值 |
|
||||||
|
|------|-----|
|
||||||
|
| 转发地址 | `100.115.195.188:50036` |
|
||||||
|
| 实际数据库 | `47.99.209.185:50036` |
|
||||||
|
| MySQL 版本 | 8.0.36 |
|
||||||
|
| 用户 | `witsoftd` |
|
||||||
|
| 密码 | `o2byaCkBvF1Y8S2L` |
|
||||||
|
| 权限 | 只读(准生产环境) |
|
||||||
|
|
||||||
|
### 连接测试结果
|
||||||
|
|
||||||
|
- ✅ Ping 可达(14-21ms)
|
||||||
|
- ✅ TCP 端口 50036 开放
|
||||||
|
- ✅ MySQL 连接成功,返回 `SELECT 1` 和版本号
|
||||||
|
|
||||||
|
### 配置文件更新
|
||||||
|
|
||||||
|
**Linux 工作区 `.env` 文件:**
|
||||||
|
```bash
|
||||||
|
# /home/yangxuan/.openclaw/workspace-sql/.env
|
||||||
|
MYSQL_PWD_SQL=5gynj20J
|
||||||
|
MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L
|
||||||
|
```
|
||||||
|
|
||||||
|
**TOOLS.md 更新:** 添加准生产数据库连接记录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Windows 端口转发持久化配置
|
||||||
|
|
||||||
|
**问题:** `netsh interface portproxy` 规则在 Windows 重启后会丢失
|
||||||
|
|
||||||
|
**解决方案:** 使用任务计划程序开机自动执行
|
||||||
|
|
||||||
|
### 配置步骤(在 Windows 100.115.195.188 上执行)
|
||||||
|
|
||||||
|
#### 1. 创建脚本目录和文件
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 创建目录
|
||||||
|
New-Item -ItemType Directory -Force -Path C:\Scripts
|
||||||
|
|
||||||
|
# 创建批处理文件 C:\Scripts\portproxy_setup.bat
|
||||||
|
@'
|
||||||
|
@echo off
|
||||||
|
REM 确保 IP Helper 服务运行
|
||||||
|
sc start iphlpsvc
|
||||||
|
|
||||||
|
REM 清理旧规则
|
||||||
|
netsh interface portproxy delete v4tov4 listenaddress=100.115.195.188 listenport=50036
|
||||||
|
netsh interface portproxy delete v4tov4 listenaddress=100.115.195.188 listenport=38848
|
||||||
|
|
||||||
|
REM 添加转发规则
|
||||||
|
netsh interface portproxy add v4tov4 listenaddress=100.115.195.188 listenport=50036 connectaddress=47.99.209.185 connectport=50036
|
||||||
|
netsh interface portproxy add v4tov4 listenaddress=100.115.195.188 listenport=38848 connectaddress=120.26.89.170 connectport=38848
|
||||||
|
|
||||||
|
echo 端口转发配置完成
|
||||||
|
'@ | Out-File -FilePath C:\Scripts\portproxy_setup.bat -Encoding ASCII
|
||||||
|
|
||||||
|
# 创建 PowerShell 启动脚本 C:\Scripts\portproxy_startup.ps1
|
||||||
|
@'
|
||||||
|
# 等待网络就绪
|
||||||
|
Start-Sleep -Seconds 10
|
||||||
|
|
||||||
|
# 执行配置
|
||||||
|
& C:\Scripts\portproxy_setup.bat
|
||||||
|
'@ | Out-File -FilePath C:\Scripts\portproxy_startup.ps1 -Encoding UTF8
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 注册任务计划程序(管理员权限)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-ExecutionPolicy Bypass -File C:\Scripts\portproxy_startup.ps1"
|
||||||
|
$trigger = New-ScheduledTaskTrigger -AtStartup
|
||||||
|
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
||||||
|
Register-ScheduledTask -TaskName "PortProxy Setup" -Action $action -Trigger $trigger -Principal $principal -Description "自动配置端口转发规则"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 重启后验证步骤
|
||||||
|
|
||||||
|
**下次 Windows 重启后,执行以下命令验证:**
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# 1. 检查任务计划程序状态
|
||||||
|
Get-ScheduledTask -TaskName "PortProxy Setup" | Select-Object TaskName, State, LastRunTime, LastTaskResult
|
||||||
|
|
||||||
|
# 2. 检查端口转发规则
|
||||||
|
netsh interface portproxy show all
|
||||||
|
|
||||||
|
# 3. 检查端口监听状态
|
||||||
|
netstat -ano | findstr "50036"
|
||||||
|
|
||||||
|
# 4. 测试 MySQL 连接
|
||||||
|
mysql -h 100.115.195.188 -P 50036 -u witsoftd -p"o2byaCkBvF1Y8S2L" -e "SELECT 1 AS test, VERSION() AS version;"
|
||||||
|
```
|
||||||
|
|
||||||
|
**期望输出:**
|
||||||
|
- 任务状态:`State = Ready`,`LastTaskResult = 0`
|
||||||
|
- 端口转发:`100.115.195.188:50036 → 47.99.209.185:50036`
|
||||||
|
- 监听状态:`TCP 100.115.195.188:50036 LISTENING`
|
||||||
|
- MySQL 测试:返回 `test=1, version=8.0.36`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 相关配置
|
||||||
|
|
||||||
|
### 环境变量设置(Windows 用户级别,永久生效)
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# PowerShell 执行(不需要管理员)
|
||||||
|
[System.Environment]::SetEnvironmentVariable('MYSQL_PWD_WIT', 'o2byaCkBvF1Y8S2L', 'User')
|
||||||
|
|
||||||
|
# 验证
|
||||||
|
echo $env:MYSQL_PWD_WIT
|
||||||
|
```
|
||||||
|
|
||||||
|
### 备用验证脚本 C:\Scripts\verify_portproxy.ps1
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Write-Host "=== 任务计划程序状态 ===" -ForegroundColor Cyan
|
||||||
|
Get-ScheduledTask -TaskName "PortProxy Setup" | Select-Object TaskName, State, LastRunTime, LastTaskResult
|
||||||
|
|
||||||
|
Write-Host "`n=== 端口转发规则 ===" -ForegroundColor Cyan
|
||||||
|
netsh interface portproxy show all
|
||||||
|
|
||||||
|
Write-Host "`n=== 端口监听状态 ===" -ForegroundColor Cyan
|
||||||
|
netstat -ano | findstr "50036"
|
||||||
|
|
||||||
|
Write-Host "`n=== MySQL 连接测试 ===" -ForegroundColor Cyan
|
||||||
|
mysql -h 100.115.195.188 -P 50036 -u witsoftd -p"o2byaCkBvF1Y8S2L" -e "SELECT 1 AS test, VERSION() AS version;"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VPS 表结构同步 - 准生产 → VPS
|
||||||
|
|
||||||
|
**时间:** 2026-08-14 13:39-14:00
|
||||||
|
|
||||||
|
**源:** 准生产 `100.115.195.188:50036` (只读)
|
||||||
|
**目标:** VPS `101.34.227.188:3306` `dmp_serp`
|
||||||
|
|
||||||
|
### 同步的表(共 5 张)
|
||||||
|
|
||||||
|
1. `dmp_om_productinfo` - 主表(ALTER TABLE)
|
||||||
|
2. `dmp_om_productinfo_cost` - 成本表(CREATE TABLE)
|
||||||
|
3. `dmp_om_productinfo_other_costs` - 其他成本表(CREATE TABLE)
|
||||||
|
4. `dmp_om_productinfo_work_costs` - 工序成本表(CREATE TABLE)
|
||||||
|
5. `dmp_om_productinfo_test` - 测试表(CREATE TABLE)
|
||||||
|
|
||||||
|
### 主表变更详情
|
||||||
|
|
||||||
|
| 字段 | 操作 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `om_discountrate` | NULL → NOT NULL | 先更新 3 条 NULL 值为 1.0 |
|
||||||
|
| `om_stockout` | NULL → NOT NULL | 先更新 NULL 值为 0 |
|
||||||
|
| `invoice_status` | 已存在 | 跳过添加 |
|
||||||
|
| `cust_product_spec` | 已存在 | 跳过添加 |
|
||||||
|
| `creator_user_id` | 删除 | 准生产无此字段 |
|
||||||
|
|
||||||
|
### 脚本位置
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/yangxuan/codes/xuan-sql/scripts/20260814/
|
||||||
|
├── README.md
|
||||||
|
├── dmp_serp__dmp_om_productinfo__struct.sql
|
||||||
|
├── dmp_serp__dmp_om_productinfo_cost__struct.sql
|
||||||
|
├── dmp_serp__dmp_om_productinfo_other_costs__struct.sql
|
||||||
|
├── dmp_serp__dmp_om_productinfo_work_costs__struct.sql
|
||||||
|
└── dmp_serp__dmp_om_productinfo_test__struct.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 执行状态
|
||||||
|
|
||||||
|
✅ 全部执行完成
|
||||||
|
|
||||||
|
### 验证结果
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 5 张表全部创建成功
|
||||||
|
SHOW TABLES LIKE 'dmp_om_productinfo%';
|
||||||
|
|
||||||
|
-- 主表关键字段验证
|
||||||
|
SELECT COLUMN_NAME, IS_NULLABLE, COLUMN_DEFAULT
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = 'dmp_serp'
|
||||||
|
AND TABLE_NAME = 'dmp_om_productinfo'
|
||||||
|
AND COLUMN_NAME IN ('om_discountrate', 'om_stockout', 'invoice_status', 'cust_product_spec');
|
||||||
|
-- om_discountrate: NO (NOT NULL)
|
||||||
|
-- om_stockout: NO (NOT NULL)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
|
||||||
|
- 不影响其他库(特别是 `ruoyi-vue-pro`)
|
||||||
|
- 字符集统一为 `utf8mb4 / utf8mb4_general_ci`
|
||||||
|
- 测试数据已保留(7 条)
|
||||||
|
|
||||||
|
## 表结构同步 - 14:33
|
||||||
|
|
||||||
|
**源:** 准生产 `dmp_smdm`
|
||||||
|
**目标:** VPS `dmp_smdm`
|
||||||
|
**处理表数:** 127
|
||||||
|
**成功:** 93
|
||||||
|
**失败:** 34
|
||||||
|
|
||||||
|
### 处理的表
|
||||||
|
|
||||||
|
| 表名 | 操作 | 状态 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
| debezium_heartbeat | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_i18n_messages | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_account_period | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_account_unit | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_arguments | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_arguments_device | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_base_config | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_batch_import_menu | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_batch_import_result | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_billrule | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_billrule_template | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_bom_closure | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_bom_master | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_company | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_currency | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_custom_print_label_template | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_custom_print_template | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_customer_bom | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_customer_classfied | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_customer_contact | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_customer_info | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_customer_login_user | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_customer_pinyin | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_customer_product_price | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_customer_user | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_department | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_device | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_device_info | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_dictionary_data | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_dictionary_type | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_dynamic_field_config | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_factory | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_invoicing_info | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_category | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_item_group_relation | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_info | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_item_info_log | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_info_replace | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_info_rule | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_info_unit | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_model | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_mold | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_pinyin | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_property | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_item_route | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_item_route_cxcl | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_item_route_op | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_location | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_mc_order_bom_header | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_mc_order_bom_line | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_modules_scenes | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_mold_info | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_operate_log | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_operation_log | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_operation_procedure | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_operation_qualities | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_order_bom_header | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_order_bom_line | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_paper_default | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_person | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_print_node | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_print_template | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_process_equip | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_process_warehouse | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_product_management_config | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_production_line | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_productionline_director | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_productive_rewources | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_property_group | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_property_item | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_property_project | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_property_project_detail | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_property_relation | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_province | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_quality_type | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_route | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_route_item_material | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_route_op | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_route_op_cycle | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_route_op_document | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_route_op_mat | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_route_op_param | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_route_op_template | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_rule_element | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_send_config | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_send_recipient | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_serial | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_serial_format | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_shift_config | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_sr_op | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_sr_user | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_standard_capacity | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_station | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_std_bom_header | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_std_bom_header_log | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_std_bom_line | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_std_bom_line_log | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_std_bom_line_logs | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_storage_category | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_storage_pinyin | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_storage_user | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_sub_storage | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_switch_config | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_third_server_config | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_unit | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_unit_conversion | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_vendor_classfied | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_vendor_info | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_md_vendor_login_user | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_vendor_pinyin | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_vendor_user | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_work_calendar | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_workgroup | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_workgroup_person | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_md_workshop | ALTER | ❌ | 执行失败 |
|
||||||
|
| dmp_optlog_infos | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_basic_config | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_basic_dictionary | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_formulation | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_formulation_material | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_formulation_product | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_ingredient | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_ingredient_material | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_requisition | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_requisition_material | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_requisition_production | CREATE | ✅ | 新建表 |
|
||||||
|
| dmp_rm_requisition_production_record | CREATE | ✅ | 新建表 |
|
||||||
|
|
||||||
|
### 脚本位置
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/yangxuan/codes/xuan-sql/scripts/20260814/
|
||||||
|
├── README.md
|
||||||
|
└── dmp_smdm__*_struct.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## table-alias 技能 - 表结构同步
|
||||||
|
|
||||||
|
**时间:** 2026-08-14 15:21-15:49
|
||||||
|
|
||||||
|
**技能版本:** v1.1.2(新建并迭代)
|
||||||
|
|
||||||
|
### 技能开发
|
||||||
|
|
||||||
|
**背景:** 需要将准生产数据库表结构同步到 VPS 开发库,确保开发环境与准生产环境表结构一致。
|
||||||
|
|
||||||
|
**创建过程:**
|
||||||
|
1. 15:21 - 创建技能目录和基础脚本
|
||||||
|
2. 15:28 - 首次执行 `dmp_serp` 全量同步(303 张表)
|
||||||
|
3. 15:34 - 发现并修复 `\n` 转义字符问题
|
||||||
|
4. 15:38 - 测试单表同步 `dmp_smes.8d_cause_action`
|
||||||
|
5. 15:40 - 修复脚本提取逻辑(`cut -f2` + `sed '1d;$d'`)
|
||||||
|
6. 15:48 - 完成 `dmp_smes` 全量同步(290 张表)
|
||||||
|
|
||||||
|
### 技术修复记录
|
||||||
|
|
||||||
|
#### v1.1.1 - `\n` 转义字符处理
|
||||||
|
|
||||||
|
**问题:** MySQL `SHOW CREATE TABLE` 返回的建表语句中,字段定义之间使用字面量 `\n` 字符(两个字符:反斜杠 + n),而非真正的换行符。
|
||||||
|
|
||||||
|
**解决:**
|
||||||
|
```bash
|
||||||
|
SOURCE_CREATE=$(mysql ... -e "SHOW CREATE TABLE ..." | cut -f2 | sed 's/\\n/\n/g')
|
||||||
|
```
|
||||||
|
|
||||||
|
**批量修复已生成脚本:**
|
||||||
|
```bash
|
||||||
|
cd /home/yangxuan/codes/xuan-sql/scripts/20260814
|
||||||
|
for f in *.sql; do sed -i 's/\\n/\n/g' "$f"; done
|
||||||
|
# 处理 351 个文件
|
||||||
|
```
|
||||||
|
|
||||||
|
#### v1.1.2 - 表体提取逻辑修复
|
||||||
|
|
||||||
|
**问题 1:** `SHOW CREATE TABLE` 输出是两列(表名 + CREATE 语句),用制表符分隔。
|
||||||
|
|
||||||
|
**解决:** 使用 `cut -f2` 提取第二列。
|
||||||
|
|
||||||
|
**问题 2:** `sed` 正则无法跨行匹配提取表体。
|
||||||
|
|
||||||
|
**解决:** 使用 `sed '1d;$d'` 删除首尾行。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 完整处理流程
|
||||||
|
SOURCE_CREATE=$(mysql ... -e "SHOW CREATE TABLE ..." | cut -f2 | sed 's/\\n/\n/g')
|
||||||
|
TABLE_BODY=$(echo "$SOURCE_CREATE" | sed '1d;$d')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 同步任务汇总
|
||||||
|
|
||||||
|
| 时间 | 源库 | 目标库 | 表数 | 成功 | 失败 | 状态 |
|
||||||
|
|------|------|--------|------|------|------|------|
|
||||||
|
| 15:28 | 准生产 `dmp_serp` | VPS `dmp_serp` | 303 | 303 | 0 | ✅ 完成 |
|
||||||
|
| 15:38 | 准生产 `dmp_smes` (单表) | VPS `dmp_smes` | 1 | 1 | 0 | ✅ 完成 |
|
||||||
|
| 15:48 | 准生产 `dmp_smes` (全量) | VPS `dmp_smes` | 290 | 290 | 0 | ✅ 完成 |
|
||||||
|
|
||||||
|
**验证结果:**
|
||||||
|
```bash
|
||||||
|
# dmp_serp
|
||||||
|
准生产:303 张表
|
||||||
|
VPS: 303 张表 ✅
|
||||||
|
|
||||||
|
# dmp_smes
|
||||||
|
准生产:290 张表
|
||||||
|
VPS: 290 张表 ✅ (原 65 张,新增 225 张)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 输出产物
|
||||||
|
|
||||||
|
**脚本目录:** `/home/yangxuan/codes/xuan-sql/scripts/20260814/`
|
||||||
|
- `dmp_serp__*__struct.sql` - 303 个文件
|
||||||
|
- `dmp_smes__*__struct.sql` - 237 个文件(新增)
|
||||||
|
- `dmp_smdm__*__struct.sql` - 127 个文件(之前生成)
|
||||||
|
|
||||||
|
**技能文件:**
|
||||||
|
- SKILL.md: `/home/yangxuan/.openclaw/workspace-sql/skills/table-alias/SKILL.md` (v1.1.2)
|
||||||
|
- 执行脚本:`/home/yangxuan/.openclaw/skills/table-alias/scripts/sync_table.sh`
|
||||||
|
|
||||||
|
### 使用示例
|
||||||
|
|
||||||
|
**自然语言命令:**
|
||||||
|
```
|
||||||
|
同步准生产 dmp_serp 的表结构到 VPS
|
||||||
|
同步准生产 dmp_smes 的 8d_cause_action 表到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
**命令行:**
|
||||||
|
```bash
|
||||||
|
export MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L
|
||||||
|
export MYSQL_PWD_SQL=5gynj20J
|
||||||
|
|
||||||
|
cd ~/.openclaw/skills/table-alias
|
||||||
|
./scripts/sync_table.sh dmp_serp # 全库同步
|
||||||
|
./scripts/sync_table.sh dmp_smes 8d_cause_action # 单表同步
|
||||||
|
```
|
||||||
|
|
||||||
|
### 注意事项
|
||||||
|
|
||||||
|
1. **网络环境:** 准生产数据库只能在 `100.115.195.x` 网段访问
|
||||||
|
2. **表数量阈值:** > 50 张表时需要用户确认
|
||||||
|
3. **错误处理:** 遇到错误时继续执行其他表
|
||||||
|
4. **表名格式:** `dmp_smes` 库的表名不带 `dmp_` 前缀(如 `8d_cause_action`)
|
||||||
|
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
---
|
||||||
|
name: "mysql-schema-sync"
|
||||||
|
description: "MySQL 表结构同步最佳实践:跨环境同步 DDL,处理 CREATE/ALTER 失败场景"
|
||||||
|
---
|
||||||
|
|
||||||
|
# MySQL 表结构同步最佳实践
|
||||||
|
|
||||||
|
## 场景
|
||||||
|
|
||||||
|
将准生产/测试环境的表结构同步到 VPS/生产环境,确保 DDL 一致。
|
||||||
|
|
||||||
|
## 核心原则
|
||||||
|
|
||||||
|
1. **表名必须带数据库前缀** - 如 `dmp_smdm.dmp_md_item_info`,因为生产实例有多个库
|
||||||
|
2. **不暴露敏感信息** - 脚本中只写库名和表名,IP/密码用环境变量
|
||||||
|
3. **先验证后执行** - 先 SELECT 确认表结构和数据量
|
||||||
|
4. **CREATE 优先于 ALTER** - 能新建不修改,减少冲突
|
||||||
|
|
||||||
|
## 脚本生成规范
|
||||||
|
|
||||||
|
### 1. 使用 mysqldump 导出结构
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 导出单个库的所有表结构(无数据)
|
||||||
|
mysqldump -h SOURCE_HOST -P PORT -u USER -p"PASSWORD" \
|
||||||
|
--no-data --skip-lock-tables \
|
||||||
|
DB_NAME > db_struct.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 脚本格式要求
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构同步脚本:table_name
|
||||||
|
-- 源:准生产 (host:port)
|
||||||
|
-- 目标:VPS (host:port) database
|
||||||
|
-- 生成时间:YYYY-MM-DD HH:MM:SS
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 表:database.table_name
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `database`.`table_name` (
|
||||||
|
-- 字段定义
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='表说明';
|
||||||
|
|
||||||
|
-- 验证
|
||||||
|
-- SHOW CREATE TABLE database.table_name\G
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 目录结构
|
||||||
|
|
||||||
|
```
|
||||||
|
xuan-sql/scripts/YYYYMMDD/
|
||||||
|
├── README.md # 同步记录(成功/失败清单)
|
||||||
|
├── database__table__struct.sql # 单表脚本
|
||||||
|
└── run_sync.sh # 批量执行脚本
|
||||||
|
```
|
||||||
|
|
||||||
|
## 执行策略
|
||||||
|
|
||||||
|
### CREATE TABLE(新建表)
|
||||||
|
|
||||||
|
✅ 成功率高,直接使用 `CREATE TABLE IF NOT EXISTS`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for sql_file in database__*_struct.sql; do
|
||||||
|
mysql -h TARGET_HOST -u root -p"PASSWORD" -D database < "$sql_file"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
### ALTER TABLE(修改表)
|
||||||
|
|
||||||
|
⚠️ 失败率高,需要特殊处理:
|
||||||
|
|
||||||
|
1. **先检查目标表是否存在**
|
||||||
|
```sql
|
||||||
|
SELECT TABLE_NAME FROM information_schema.TABLES
|
||||||
|
WHERE TABLE_SCHEMA = 'database' AND TABLE_NAME = 'table_name';
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **对比结构差异**
|
||||||
|
```sql
|
||||||
|
-- 源表结构
|
||||||
|
SHOW CREATE TABLE source.table_name\G
|
||||||
|
-- 目标表结构
|
||||||
|
SHOW CREATE TABLE target.table_name\G
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **生成差异脚本**(只修改不同的字段/索引)
|
||||||
|
|
||||||
|
4. **外键约束延迟添加**
|
||||||
|
- 先执行字段/索引变更
|
||||||
|
- 所有表完成后,再添加外键
|
||||||
|
|
||||||
|
## 常见失败原因及处理
|
||||||
|
|
||||||
|
| 错误类型 | 原因 | 解决方案 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 字段已存在 | 目标表已有同名字段 | 跳过或使用 `ALTER TABLE ... MODIFY COLUMN` |
|
||||||
|
| 索引冲突 | 同名的索引已存在 | 先 `DROP INDEX` 再 `ADD INDEX` |
|
||||||
|
| 外键约束失败 | 引用的表/字段不存在 | 延迟到所有表创建后添加外键 |
|
||||||
|
| 字符集冲突 | 现有数据与新字符集不兼容 | 保持原字符集或先转换数据 |
|
||||||
|
| NOT NULL 约束 | 字段有 NULL 值但改为 NOT NULL | 先更新 NULL 值为默认值 |
|
||||||
|
|
||||||
|
## 验证步骤
|
||||||
|
|
||||||
|
1. **表数量对比**
|
||||||
|
```sql
|
||||||
|
SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'database';
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **结构对比**
|
||||||
|
```sql
|
||||||
|
SHOW CREATE TABLE database.table_name\G
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **关键字段验证**
|
||||||
|
```sql
|
||||||
|
SELECT COLUMN_NAME, IS_NULLABLE, COLUMN_DEFAULT, DATA_TYPE
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = 'database' AND TABLE_NAME = 'table_name';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 回滚方案
|
||||||
|
|
||||||
|
1. **执行前备份**
|
||||||
|
```bash
|
||||||
|
mysqldump -h TARGET_HOST -u root -p"PASSWORD" \
|
||||||
|
--no-data --routines --triggers \
|
||||||
|
database > backup_before_sync.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **记录执行日志**
|
||||||
|
```bash
|
||||||
|
./run_sync.sh > sync_log.txt 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **失败表单独处理**
|
||||||
|
- 记录失败的表和错误信息
|
||||||
|
- 人工分析后生成修复脚本
|
||||||
|
|
||||||
|
## 示例:dmp_smdm 同步(2026-08-14)
|
||||||
|
|
||||||
|
**源:** 准生产 `100.115.195.188:50036`
|
||||||
|
**目标:** VPS `101.34.227.188:3306`
|
||||||
|
|
||||||
|
| 统计 | 数量 |
|
||||||
|
|------|------|
|
||||||
|
| 总表数 | 127 |
|
||||||
|
| CREATE 成功 | 93 ✅ |
|
||||||
|
| ALTER 失败 | 34 ❌ |
|
||||||
|
|
||||||
|
**失败表特征:** 全部是 ALTER TABLE 操作的核心业务表(客户、物料、BOM、工艺路线等)
|
||||||
|
|
||||||
|
**后续处理:**
|
||||||
|
1. 人工对比失败表的源/目标结构差异
|
||||||
|
2. 生成针对性的 ALTER 脚本(只修改必要字段)
|
||||||
|
3. 在业务低峰期执行变更
|
||||||
|
|
||||||
|
## 相关技能
|
||||||
|
|
||||||
|
- sql-toolkit: SQL 查询、设计、迁移
|
||||||
|
- mysql-ddl-backup: 定时备份表结构
|
||||||
@@ -0,0 +1,274 @@
|
|||||||
|
# 使用示例
|
||||||
|
|
||||||
|
## 自然语言触发
|
||||||
|
|
||||||
|
### 示例 1:同步 dmp_serp
|
||||||
|
|
||||||
|
**用户:**
|
||||||
|
```
|
||||||
|
同步准生产 dmp_serp 的表结构到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
**技能响应:**
|
||||||
|
```
|
||||||
|
开始执行表结构对齐...
|
||||||
|
|
||||||
|
源数据库:准生产 (100.115.195.188:50036) dmp_serp
|
||||||
|
目标数据库:VPS (101.34.227.188:3306) dmp_serp
|
||||||
|
|
||||||
|
[1/5] 验证数据库连接...
|
||||||
|
✓ 源数据库连接成功
|
||||||
|
✓ 目标数据库连接成功
|
||||||
|
|
||||||
|
[2/5] 获取源数据库表列表...
|
||||||
|
找到 45 张表
|
||||||
|
|
||||||
|
[3/5] 对比并生成脚本...
|
||||||
|
处理表:dmp_om_productinfo
|
||||||
|
→ 目标表已存在,对比结构差异...
|
||||||
|
→ 发现结构差异,生成 ALTER 语句
|
||||||
|
✓ ALTER TABLE 成功
|
||||||
|
|
||||||
|
处理表:dmp_om_productinfo_cost
|
||||||
|
→ 目标表不存在,执行 CREATE TABLE
|
||||||
|
✓ CREATE TABLE 成功
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
[4/5] 生成 README.md...
|
||||||
|
[5/5] 记录到记忆文件...
|
||||||
|
|
||||||
|
============================================================
|
||||||
|
执行完成!
|
||||||
|
============================================================
|
||||||
|
总表数:45
|
||||||
|
成功:45
|
||||||
|
失败:0
|
||||||
|
脚本目录:/home/yangxuan/codes/xuan-sql/scripts/20260814
|
||||||
|
记忆文件:/home/yangxuan/.openclaw/workspace-sql/memory/2026-08-14.md
|
||||||
|
============================================================
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 示例 2:同步 dmp_smdm
|
||||||
|
|
||||||
|
**用户:**
|
||||||
|
```
|
||||||
|
同步准生产 dmp_smdm 的表结构到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
**执行:** 同上,目标库为 `dmp_smdm`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 命令行直接执行
|
||||||
|
|
||||||
|
### 基本用法
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/yangxuan/.openclaw/workspace-sql/skills/table-alias
|
||||||
|
|
||||||
|
# 同步 dmp_serp
|
||||||
|
./sync_tables.sh dmp_serp
|
||||||
|
|
||||||
|
# 同步 dmp_smdm
|
||||||
|
./sync_tables.sh dmp_smdm
|
||||||
|
|
||||||
|
# 指定不同的目标库名(少见场景)
|
||||||
|
./sync_tables.sh dmp_serp dmp_serp_dev
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置文件修改
|
||||||
|
|
||||||
|
### 添加新的源数据库
|
||||||
|
|
||||||
|
编辑 `~/.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
|
||||||
|
|
||||||
|
# 添加新的源
|
||||||
|
- name: 生产备份
|
||||||
|
host: 47.99.209.185
|
||||||
|
port: 3306
|
||||||
|
user: backup_user
|
||||||
|
password_env: MYSQL_PWD_BACKUP
|
||||||
|
readonly: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### 添加新的目标数据库
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
targets:
|
||||||
|
- name: VPS
|
||||||
|
host: 101.34.227.188
|
||||||
|
port: 3306
|
||||||
|
user: root
|
||||||
|
password_env: MYSQL_PWD_SQL
|
||||||
|
readonly: false
|
||||||
|
blacklist:
|
||||||
|
- ruoyi-vue-pro
|
||||||
|
|
||||||
|
# 添加新的目标
|
||||||
|
- name: 本地测试
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 3306
|
||||||
|
user: root
|
||||||
|
password_env: MYSQL_PWD_LOCAL
|
||||||
|
readonly: false
|
||||||
|
blacklist:
|
||||||
|
- mysql
|
||||||
|
- sys
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 输出文件示例
|
||||||
|
|
||||||
|
### SQL 脚本文件
|
||||||
|
|
||||||
|
`20260814/dmp_serp__dmp_om_productinfo__struct.sql`:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构同步脚本:dmp_om_productinfo
|
||||||
|
-- 源:准生产 (100.115.195.188:50036)
|
||||||
|
-- 目标:VPS (101.34.227.188:3306) dmp_serp
|
||||||
|
-- 生成时间:2026-08-14 14:00:00
|
||||||
|
-- 操作:ALTER TABLE(结构差异)
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
USE dmp_serp;
|
||||||
|
-- 结构差异:
|
||||||
|
-- - 添加字段:cust_product_spec
|
||||||
|
-- - 添加字段:invoice_status
|
||||||
|
-- - 删除字段:creator_user_id
|
||||||
|
|
||||||
|
ALTER TABLE dmp_om_productinfo ADD COLUMN cust_product_spec varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '客户产品规格' AFTER invoice_status;
|
||||||
|
|
||||||
|
ALTER TABLE dmp_om_productinfo ADD COLUMN invoice_status varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'Y' COMMENT 'Y/N 是否开票' AFTER order_change_flag;
|
||||||
|
|
||||||
|
ALTER TABLE dmp_om_productinfo DROP COLUMN creator_user_id;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 验证
|
||||||
|
-- ============================================================
|
||||||
|
-- SHOW CREATE TABLE dmp_om_productinfo\G
|
||||||
|
```
|
||||||
|
|
||||||
|
### README.md
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 表结构同步脚本 - 20260814
|
||||||
|
|
||||||
|
**源:** 准生产 `100.115.195.188:50036` `dmp_serp`
|
||||||
|
**目标:** VPS `101.34.227.188:3306` `dmp_serp`
|
||||||
|
**生成时间:** 2026-08-14 14:00:00
|
||||||
|
**执行状态:** ✅ 已完成
|
||||||
|
|
||||||
|
## 脚本清单
|
||||||
|
|
||||||
|
| 序号 | 脚本文件 | 操作类型 | 状态 | 说明 |
|
||||||
|
|------|---------|---------|------|------|
|
||||||
|
| 1 | `dmp_serp__dmp_om_productinfo__struct.sql` | ALTER | ✅ | 3 处变更 |
|
||||||
|
| 2 | `dmp_serp__dmp_om_productinfo_cost__struct.sql` | CREATE | ✅ | 新建表 |
|
||||||
|
| ... | ... | ... | ... | ... |
|
||||||
|
|
||||||
|
## 执行结果
|
||||||
|
|
||||||
|
- 总表数:45
|
||||||
|
- 成功:45
|
||||||
|
- 失败:0
|
||||||
|
|
||||||
|
## 验证命令
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 检查表列表
|
||||||
|
SHOW TABLES LIKE 'dmp_serp%';
|
||||||
|
|
||||||
|
-- 检查表结构
|
||||||
|
SHOW CREATE TABLE dmp_om_productinfo\G
|
||||||
|
```
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q1: 密码环境变量未设置
|
||||||
|
|
||||||
|
**错误:**
|
||||||
|
```
|
||||||
|
错误:密码环境变量未设置
|
||||||
|
请确保已设置:MYSQL_PWD_WIT 和 MYSQL_PWD_SQL
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决:**
|
||||||
|
```bash
|
||||||
|
# 在 ~/.bashrc 或 ~/.zshrc 中添加
|
||||||
|
export MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L
|
||||||
|
export MYSQL_PWD_SQL=5gynj20J
|
||||||
|
|
||||||
|
# 重新加载
|
||||||
|
source ~/.bashrc # 或 source ~/.zshrc
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Q2: 表数量超过阈值
|
||||||
|
|
||||||
|
**提示:**
|
||||||
|
```
|
||||||
|
表数量超过 50 张(当前:80),是否继续?[y/N]
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决:** 输入 `y` 继续,或按 `Ctrl+C` 取消
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Q3: ALTER TABLE 失败
|
||||||
|
|
||||||
|
**错误:**
|
||||||
|
```
|
||||||
|
处理表:dmp_om_productinfo
|
||||||
|
→ 发现结构差异,生成 ALTER 语句
|
||||||
|
✗ ALTER TABLE 失败(继续执行下一个表)
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因:** 可能是字段类型冲突、有 NULL 值等
|
||||||
|
|
||||||
|
**解决:**
|
||||||
|
1. 查看生成的 SQL 脚本
|
||||||
|
2. 手动修复数据(如更新 NULL 值)
|
||||||
|
3. 重新执行脚本
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Q4: 连接超时
|
||||||
|
|
||||||
|
**错误:**
|
||||||
|
```
|
||||||
|
错误:无法连接到源数据库(准生产)
|
||||||
|
```
|
||||||
|
|
||||||
|
**解决:**
|
||||||
|
1. 检查 Windows 端口转发是否运行
|
||||||
|
2. 检查防火墙设置
|
||||||
|
3. 验证密码是否正确
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- [SKILL.md](SKILL.md) - 技能详细说明
|
||||||
|
- [../.db-sync-config.yaml](../.db-sync-config.yaml) - 配置文件
|
||||||
|
- [sync_tables.sh](sync_tables.sh) - 执行脚本
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
# table-alias 技能
|
||||||
|
|
||||||
|
**表结构对齐技能** - 将准生产数据库表结构同步到 VPS 开发库
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 1. 自然语言触发
|
||||||
|
|
||||||
|
```
|
||||||
|
同步准生产 dmp_serp 的表结构到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 命令行执行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/yangxuan/.openclaw/workspace-sql/skills/table-alias
|
||||||
|
./sync_tables.sh dmp_serp
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
| 文件 | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| `SKILL.md` | 技能完整说明文档 |
|
||||||
|
| `sync_tables.sh` | 执行脚本 |
|
||||||
|
| `EXAMPLES.md` | 使用示例和常见问题 |
|
||||||
|
| `README.md` | 本文件(快速开始) |
|
||||||
|
| `../.db-sync-config.yaml` | 配置文件 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置
|
||||||
|
|
||||||
|
确保已设置环境变量:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ~/.bashrc 或 ~/.zshrc
|
||||||
|
export MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L # 准生产密码
|
||||||
|
export MYSQL_PWD_SQL=5gynj20J # VPS 密码
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 输出
|
||||||
|
|
||||||
|
**脚本位置:** `/home/yangxuan/codes/xuan-sql/scripts/{日期}/`
|
||||||
|
|
||||||
|
**记忆文件:** `/home/yangxuan/.openclaw/workspace-sql/memory/{日期}.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- [SKILL.md](SKILL.md) - 完整技能说明
|
||||||
|
- [EXAMPLES.md](EXAMPLES.md) - 使用示例
|
||||||
@@ -0,0 +1,482 @@
|
|||||||
|
# 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_name<TAB>CREATE 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 <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. 获取表列表
|
||||||
|
|
||||||
|
从源数据库获取所有表:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SHOW TABLES FROM <source_database>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 对比并生成脚本
|
||||||
|
|
||||||
|
对每个表执行:
|
||||||
|
|
||||||
|
#### 5.1 读取源表结构
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SHOW CREATE TABLE <source_database>.<table_name>\G
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5.2 读取目标表结构
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 检查表是否存在
|
||||||
|
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` |
|
||||||
|
|
||||||
|
**对比算法:**
|
||||||
|
|
||||||
|
```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 <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`:
|
||||||
|
|
||||||
|
```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 | 初始版本 |
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# table-alias 技能 - 快速开始
|
||||||
|
|
||||||
|
## 自然语言触发
|
||||||
|
|
||||||
|
```
|
||||||
|
同步准生产 dmp_serp 的表结构到 VPS
|
||||||
|
```
|
||||||
|
|
||||||
|
## 命令行执行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd skills/table-alias
|
||||||
|
./sync_tables.sh dmp_serp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置环境变量
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export MYSQL_PWD_WIT=o2byaCkBvF1Y8S2L
|
||||||
|
export MYSQL_PWD_SQL=5gynj20J
|
||||||
|
```
|
||||||
|
|
||||||
|
## 输出位置
|
||||||
|
|
||||||
|
- 脚本:`scripts/{日期}/`
|
||||||
|
- 记忆:`memory/{日期}.md`
|
||||||
+187
@@ -0,0 +1,187 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 表结构对齐脚本 - 准生产 → VPS
|
||||||
|
# 支持单表模式,避免全量同步
|
||||||
|
|
||||||
|
# 不使用 set -e,手动处理错误
|
||||||
|
|
||||||
|
# 连接信息
|
||||||
|
SOURCE_HOST="100.115.195.188"
|
||||||
|
SOURCE_PORT="50036"
|
||||||
|
SOURCE_USER="witsoftd"
|
||||||
|
TARGET_HOST="101.34.227.188"
|
||||||
|
TARGET_PORT="3306"
|
||||||
|
TARGET_USER="root"
|
||||||
|
|
||||||
|
# 颜色输出
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
||||||
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||||
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
echo "用法:$0 <database> <table>"
|
||||||
|
echo " $0 <database> # 同步整个库"
|
||||||
|
echo ""
|
||||||
|
echo "示例:"
|
||||||
|
echo " $0 dmp_serp idb_pub_basic_item_info # 单表同步"
|
||||||
|
echo " $0 dmp_serp # 全库同步"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -z "$1" ] && usage
|
||||||
|
|
||||||
|
SOURCE_DB="$1"
|
||||||
|
TABLE="$2"
|
||||||
|
TARGET_DB="${3:-$SOURCE_DB}" # 可选第三个参数指定目标库名,默认同源库名
|
||||||
|
|
||||||
|
# 检查环境变量
|
||||||
|
if [ -z "$MYSQL_PWD_WIT" ]; then
|
||||||
|
log_error "缺少环境变量 MYSQL_PWD_WIT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ -z "$MYSQL_PWD_SQL" ]; then
|
||||||
|
log_error "缺少环境变量 MYSQL_PWD_SQL"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 输出目录
|
||||||
|
DATE=$(date +%Y%m%d)
|
||||||
|
OUTPUT_DIR="/home/yangxuan/codes/xuan-sql/scripts/${DATE}"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
log_info "同步方向:准生产 (${SOURCE_HOST}:${SOURCE_PORT}) → VPS (${TARGET_HOST}:${TARGET_PORT})"
|
||||||
|
log_info "数据库:${SOURCE_DB}"
|
||||||
|
[ -n "$TABLE" ] && log_info "表:${TABLE} (单表模式)"
|
||||||
|
|
||||||
|
# 获取表列表
|
||||||
|
if [ -n "$TABLE" ]; then
|
||||||
|
# 单表模式
|
||||||
|
log_info "单表模式:只同步 ${TABLE}"
|
||||||
|
TABLES="$TABLE"
|
||||||
|
else
|
||||||
|
# 全库模式
|
||||||
|
log_info "全库模式:获取表列表..."
|
||||||
|
TABLES=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW TABLES;")
|
||||||
|
TABLE_COUNT=$(echo "$TABLES" | wc -l)
|
||||||
|
log_info "共 ${TABLE_COUNT} 张表"
|
||||||
|
|
||||||
|
if [ "$TABLE_COUNT" -gt 50 ]; then
|
||||||
|
log_warn "表数量超过 50,确认继续?(y/n)"
|
||||||
|
read -r CONFIRM
|
||||||
|
[ "$CONFIRM" != "y" ] && exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 同步单表函数
|
||||||
|
sync_single_table() {
|
||||||
|
local TABLE_NAME="$1"
|
||||||
|
local SCRIPT_FILE="${OUTPUT_DIR}/${SOURCE_DB}__${TABLE_NAME}__struct.sql"
|
||||||
|
|
||||||
|
log_info "处理:${TABLE_NAME}"
|
||||||
|
|
||||||
|
# 检查源表是否存在
|
||||||
|
SOURCE_EXISTS=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e \
|
||||||
|
"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$SOURCE_DB' AND TABLE_NAME='$TABLE_NAME';")
|
||||||
|
|
||||||
|
if [ "$SOURCE_EXISTS" -eq 0 ]; then
|
||||||
|
log_error "源表 ${SOURCE_DB}.${TABLE_NAME} 不存在"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查目标表是否存在
|
||||||
|
TARGET_EXISTS=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" -D "$TARGET_DB" -N -e \
|
||||||
|
"SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$TARGET_DB' AND TABLE_NAME='$TABLE_NAME';")
|
||||||
|
|
||||||
|
# 获取源表结构
|
||||||
|
SOURCE_CREATE=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW CREATE TABLE \`${TABLE_NAME}\`;" | tail -1)
|
||||||
|
|
||||||
|
if [ "$TARGET_EXISTS" -eq 0 ]; then
|
||||||
|
# 表不存在 → CREATE
|
||||||
|
log_info " 目标表不存在,使用 CREATE TABLE"
|
||||||
|
cat > "$SCRIPT_FILE" <<EOF
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构同步:${TABLE_NAME}
|
||||||
|
-- 源:准生产 (${SOURCE_HOST}:${SOURCE_PORT})
|
||||||
|
-- 目标:VPS (${TARGET_HOST}:${TARGET_PORT}) ${TARGET_DB}
|
||||||
|
-- 生成时间:$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS \`${TARGET_DB}\`.\`${TABLE_NAME}\` (
|
||||||
|
${SOURCE_CREATE#*CREATE TABLE \`$TABLE_NAME\` (}
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
# 表存在 → 对比差异
|
||||||
|
log_info " 目标表已存在,对比结构差异..."
|
||||||
|
|
||||||
|
# 获取字段差异
|
||||||
|
DIFF_FIELDS=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "
|
||||||
|
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA='$SOURCE_DB' AND TABLE_NAME='$TABLE_NAME'
|
||||||
|
ORDER BY ORDINAL_POSITION;
|
||||||
|
" | md5sum)
|
||||||
|
|
||||||
|
TARGET_DIFF=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" -D "$TARGET_DB" -N -e "
|
||||||
|
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_DEFAULT, COLUMN_COMMENT
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA='$TARGET_DB' AND TABLE_NAME='$TABLE_NAME'
|
||||||
|
ORDER BY ORDINAL_POSITION;
|
||||||
|
" | md5sum)
|
||||||
|
|
||||||
|
if [ "$DIFF_FIELDS" = "$TARGET_DIFF" ]; then
|
||||||
|
log_info " 结构一致,跳过"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_warn " 发现差异,生成 ALTER 脚本..."
|
||||||
|
# TODO: 生成精确的 ALTER 语句
|
||||||
|
SOURCE_STRUCT=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW CREATE TABLE \`$TABLE_NAME\`;" 2>/dev/null | tail -1 || echo "无法获取")
|
||||||
|
TARGET_STRUCT=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" -D "$TARGET_DB" -N -e "SHOW CREATE TABLE \`$TABLE_NAME\`;" 2>/dev/null | tail -1 || echo "无法获取")
|
||||||
|
cat > "$SCRIPT_FILE" <<EOF
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构修改:${TABLE_NAME}
|
||||||
|
-- 源:准生产 (${SOURCE_HOST}:${SOURCE_PORT})
|
||||||
|
-- 目标:VPS (${TARGET_HOST}:${TARGET_PORT}) ${TARGET_DB}
|
||||||
|
-- 生成时间:$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
-- ============================================================
|
||||||
|
-- ⚠️ 需要人工对比差异后手动完善 ALTER 语句
|
||||||
|
|
||||||
|
-- 源表结构:
|
||||||
|
-- $SOURCE_STRUCT
|
||||||
|
|
||||||
|
-- 目标表结构:
|
||||||
|
-- $TARGET_STRUCT
|
||||||
|
|
||||||
|
-- ALTER TABLE \`${TARGET_DB}\`.\`${TABLE_NAME}\` ...;
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 执行脚本
|
||||||
|
if [ -f "$SCRIPT_FILE" ]; then
|
||||||
|
log_info " 执行脚本:${SCRIPT_FILE}"
|
||||||
|
if mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"${MYSQL_PWD_SQL}" < "$SCRIPT_FILE" 2>&1; then
|
||||||
|
log_info " ✅ 成功"
|
||||||
|
else
|
||||||
|
log_error " ❌ 失败,查看脚本:${SCRIPT_FILE}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 执行同步
|
||||||
|
FAILED=0
|
||||||
|
SUCCESS=0
|
||||||
|
|
||||||
|
for TABLE_NAME in $TABLES; do
|
||||||
|
if sync_single_table "$TABLE_NAME"; then
|
||||||
|
((SUCCESS++))
|
||||||
|
else
|
||||||
|
((FAILED++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log_info "完成!成功:${SUCCESS}, 失败:${FAILED}"
|
||||||
|
log_info "脚本目录:${OUTPUT_DIR}"
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# 表结构对齐脚本 - 准生产 → VPS
|
||||||
|
|
||||||
|
SOURCE_DB="${1:-}"
|
||||||
|
TARGET_DB="${2:-$SOURCE_DB}"
|
||||||
|
|
||||||
|
[ -z "$SOURCE_DB" ] && echo "用法:$0 <database>" && exit 1
|
||||||
|
|
||||||
|
# 连接信息
|
||||||
|
SOURCE_HOST="100.115.195.188"
|
||||||
|
SOURCE_PORT="50036"
|
||||||
|
SOURCE_USER="witsoftd"
|
||||||
|
TARGET_HOST="101.34.227.188"
|
||||||
|
TARGET_PORT="3306"
|
||||||
|
TARGET_USER="root"
|
||||||
|
|
||||||
|
echo "同步 $SOURCE_DB 到 VPS..."
|
||||||
|
|
||||||
|
# 获取表列表
|
||||||
|
TABLES=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"${MYSQL_PWD_WIT}" -D "$SOURCE_DB" -N -e "SHOW TABLES;")
|
||||||
|
|
||||||
|
for TABLE in $TABLES; do
|
||||||
|
echo "处理:$TABLE"
|
||||||
|
# 对比并生成脚本...
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "完成!"
|
||||||
+344
@@ -0,0 +1,344 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================
|
||||||
|
# 表结构对齐脚本 - 准生产 → VPS
|
||||||
|
# ============================================================
|
||||||
|
# 用法:./sync_tables.sh <source_database> [target_database]
|
||||||
|
# 示例:./sync_tables.sh dmp_serp
|
||||||
|
# ./sync_tables.sh dmp_smdm dmp_smdm
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 去掉 set -e,允许错误继续执行
|
||||||
|
|
||||||
|
# 加载配置
|
||||||
|
CONFIG_FILE="/home/yangxuan/.openclaw/workspace-sql/.db-sync-config.yaml"
|
||||||
|
SCRIPT_DIR="/home/yangxuan/codes/xuan-sql/scripts"
|
||||||
|
MEMORY_DIR="/home/yangxuan/.openclaw/workspace-sql/memory"
|
||||||
|
|
||||||
|
# 解析参数
|
||||||
|
SOURCE_DB="${1:-}"
|
||||||
|
TARGET_DB="${2:-$SOURCE_DB}"
|
||||||
|
|
||||||
|
if [ -z "$SOURCE_DB" ]; then
|
||||||
|
echo "用法:$0 <source_database> [target_database]"
|
||||||
|
echo "示例:$0 dmp_serp"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 从配置文件读取连接信息(简单 YAML 解析)
|
||||||
|
SOURCE_HOST=$(grep -A5 "name: 准生产" "$CONFIG_FILE" | grep "host:" | head -1 | awk '{print $2}')
|
||||||
|
SOURCE_PORT=$(grep -A5 "name: 准生产" "$CONFIG_FILE" | grep "port:" | head -1 | awk '{print $2}')
|
||||||
|
SOURCE_USER=$(grep -A5 "name: 准生产" "$CONFIG_FILE" | grep "user:" | head -1 | awk '{print $2}')
|
||||||
|
SOURCE_PWD_ENV=$(grep -A5 "name: 准生产" "$CONFIG_FILE" | grep "password_env:" | head -1 | awk '{print $2}')
|
||||||
|
|
||||||
|
TARGET_HOST=$(grep -A5 "name: VPS" "$CONFIG_FILE" | grep "host:" | head -1 | awk '{print $2}')
|
||||||
|
TARGET_PORT=$(grep -A5 "name: VPS" "$CONFIG_FILE" | grep "port:" | head -1 | awk '{print $2}')
|
||||||
|
TARGET_USER=$(grep -A5 "name: VPS" "$CONFIG_FILE" | grep "user:" | head -1 | awk '{print $2}')
|
||||||
|
TARGET_PWD_ENV=$(grep -A5 "name: VPS" "$CONFIG_FILE" | grep "password_env:" | head -1 | awk '{print $2}')
|
||||||
|
|
||||||
|
# 获取密码
|
||||||
|
SOURCE_PWD="${!SOURCE_PWD_ENV}"
|
||||||
|
TARGET_PWD="${!TARGET_PWD_ENV}"
|
||||||
|
|
||||||
|
if [ -z "$SOURCE_PWD" ] || [ -z "$TARGET_PWD" ]; then
|
||||||
|
echo "错误:密码环境变量未设置"
|
||||||
|
echo "请确保已设置:$SOURCE_PWD_ENV 和 $TARGET_PWD_ENV"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 生成输出目录
|
||||||
|
DATE_STR=$(date +%Y%m%d)
|
||||||
|
OUTPUT_DIR="$SCRIPT_DIR/$DATE_STR"
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
echo "============================================================"
|
||||||
|
echo "表结构对齐 - 准生产 → VPS"
|
||||||
|
echo "============================================================"
|
||||||
|
echo "源数据库:$SOURCE_HOST:$SOURCE_PORT / $SOURCE_DB"
|
||||||
|
echo "目标数据库:$TARGET_HOST:$TARGET_PORT / $TARGET_DB"
|
||||||
|
echo "输出目录:$OUTPUT_DIR"
|
||||||
|
echo "============================================================"
|
||||||
|
|
||||||
|
# 验证连接
|
||||||
|
echo ""
|
||||||
|
echo "[1/5] 验证数据库连接..."
|
||||||
|
|
||||||
|
# 验证源库
|
||||||
|
if ! mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"$SOURCE_PWD" -e "SELECT 1;" > /dev/null 2>&1; then
|
||||||
|
echo "错误:无法连接到源数据库(准生产)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ 源数据库连接成功"
|
||||||
|
|
||||||
|
# 验证目标库
|
||||||
|
if ! mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"$TARGET_PWD" -e "SELECT 1;" > /dev/null 2>&1; then
|
||||||
|
echo "错误:无法连接到目标数据库(VPS)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "✓ 目标数据库连接成功"
|
||||||
|
|
||||||
|
# 获取表列表
|
||||||
|
echo ""
|
||||||
|
echo "[2/5] 获取源数据库表列表..."
|
||||||
|
|
||||||
|
TABLES=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"$SOURCE_PWD" -D "$SOURCE_DB" -N -e "SHOW TABLES;" 2>/dev/null)
|
||||||
|
TABLE_COUNT=$(echo "$TABLES" | wc -l)
|
||||||
|
|
||||||
|
echo "找到 $TABLE_COUNT 张表"
|
||||||
|
|
||||||
|
if [ "$TABLE_COUNT" -gt 50 ]; then
|
||||||
|
read -p "表数量超过 50 张(当前:$TABLE_COUNT),是否继续?[y/N] " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||||
|
echo "已取消"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 初始化统计
|
||||||
|
SUCCESS_COUNT=0
|
||||||
|
FAIL_COUNT=0
|
||||||
|
declare -a TABLE_STATUS=()
|
||||||
|
|
||||||
|
# 处理每个表
|
||||||
|
echo ""
|
||||||
|
echo "[3/5] 对比并生成脚本..."
|
||||||
|
|
||||||
|
for TABLE in $TABLES; do
|
||||||
|
echo ""
|
||||||
|
echo "处理表:$TABLE"
|
||||||
|
|
||||||
|
SCRIPT_FILE="$OUTPUT_DIR/${TARGET_DB}__${TABLE}__struct.sql"
|
||||||
|
|
||||||
|
# 获取源表结构
|
||||||
|
SOURCE_DDL=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"$SOURCE_PWD" -D "$SOURCE_DB" -N -e "SHOW CREATE TABLE $TABLE\G" 2>/dev/null)
|
||||||
|
|
||||||
|
# 检查目标表是否存在
|
||||||
|
TARGET_EXISTS=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"$TARGET_PWD" -D "$TARGET_DB" -N -e "SHOW TABLES LIKE '$TABLE';" 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
if [ "$TARGET_EXISTS" -eq 0 ]; then
|
||||||
|
# 目标表不存在,创建表
|
||||||
|
echo " → 目标表不存在,执行 CREATE TABLE"
|
||||||
|
|
||||||
|
# 提取 CREATE TABLE 语句
|
||||||
|
CREATE_STMT=$(echo "$SOURCE_DDL" | grep -A1000 "Create Table:" | tail -n +2 | sed 's/^ //')
|
||||||
|
|
||||||
|
# 生成脚本
|
||||||
|
cat > "$SCRIPT_FILE" << EOF
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构同步脚本:$TABLE
|
||||||
|
-- 源:准生产 ($SOURCE_HOST:$SOURCE_PORT)
|
||||||
|
-- 目标:VPS ($TARGET_HOST:$TARGET_PORT) $TARGET_DB
|
||||||
|
-- 生成时间:$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
-- 操作:CREATE TABLE(目标表不存在)
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
USE $TARGET_DB;
|
||||||
|
|
||||||
|
$CREATE_STMT
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 验证
|
||||||
|
-- ============================================================
|
||||||
|
-- SHOW CREATE TABLE $TABLE\\G
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 执行脚本
|
||||||
|
if mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"$TARGET_PWD" -D "$TARGET_DB" < "$SCRIPT_FILE" 2>/dev/null; then
|
||||||
|
echo " ✓ CREATE TABLE 成功"
|
||||||
|
((SUCCESS_COUNT++))
|
||||||
|
TABLE_STATUS+=("| $TABLE | CREATE | ✅ | 新建表 |")
|
||||||
|
else
|
||||||
|
echo " ✗ CREATE TABLE 失败"
|
||||||
|
((FAIL_COUNT++))
|
||||||
|
TABLE_STATUS+=("| $TABLE | CREATE | ❌ | 创建失败 |")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# 目标表已存在,对比结构
|
||||||
|
echo " → 目标表已存在,对比结构差异..."
|
||||||
|
|
||||||
|
TARGET_DDL=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"$TARGET_PWD" -D "$TARGET_DB" -N -e "SHOW CREATE TABLE $TABLE\G" 2>/dev/null)
|
||||||
|
|
||||||
|
# 获取字段列表对比
|
||||||
|
SOURCE_FIELDS=$(mysql -h "$SOURCE_HOST" -P "$SOURCE_PORT" -u "$SOURCE_USER" -p"$SOURCE_PWD" -D "$SOURCE_DB" -N -e "
|
||||||
|
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, IFNULL(COLUMN_DEFAULT,'NULL'), IFNULL(COLUMN_COMMENT,'')
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = '$SOURCE_DB' AND TABLE_NAME = '$TABLE'
|
||||||
|
ORDER BY ORDINAL_POSITION;
|
||||||
|
" 2>/dev/null)
|
||||||
|
|
||||||
|
TARGET_FIELDS=$(mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"$TARGET_PWD" -D "$TARGET_DB" -N -e "
|
||||||
|
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, IFNULL(COLUMN_DEFAULT,'NULL'), IFNULL(COLUMN_COMMENT,'')
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = '$TARGET_DB' AND TABLE_NAME = '$TABLE'
|
||||||
|
ORDER BY ORDINAL_POSITION;
|
||||||
|
" 2>/dev/null)
|
||||||
|
|
||||||
|
# 生成 ALTER 语句(简化版:只处理字段增删改)
|
||||||
|
ALTER_STMT=""
|
||||||
|
CHANGES=""
|
||||||
|
|
||||||
|
# 检查源有目标无的字段(添加)
|
||||||
|
while IFS='|' read -r COL_NAME COL_TYPE IS_NULL COL_DEF COL_COMMENT; do
|
||||||
|
if ! echo "$TARGET_FIELDS" | grep -q "^$COL_NAME|"; then
|
||||||
|
# 字段不存在,添加
|
||||||
|
NULLABLE="$([ "$IS_NULL" = "YES" ] && echo "NULL" || echo "NOT NULL")"
|
||||||
|
DEFAULT="$([ "$COL_DEF" != "NULL" ] && echo "DEFAULT $COL_DEF" || echo "")"
|
||||||
|
COMMENT="$([ -n "$COL_COMMENT" ] && echo "COMMENT '$COL_COMMENT'" || echo "")"
|
||||||
|
|
||||||
|
ALTER_STMT="$ALTER_STMT
|
||||||
|
ALTER TABLE $TABLE ADD COLUMN $COL_NAME $COL_TYPE $NULLABLE $DEFAULT $COMMENT;"
|
||||||
|
CHANGES="$CHANGES\n - 添加字段:$COL_NAME"
|
||||||
|
fi
|
||||||
|
done <<< "$SOURCE_FIELDS"
|
||||||
|
|
||||||
|
# 检查目标有源无的字段(删除)
|
||||||
|
while IFS='|' read -r COL_NAME COL_TYPE IS_NULL COL_DEF COL_COMMENT; do
|
||||||
|
if ! echo "$SOURCE_FIELDS" | grep -q "^$COL_NAME|"; then
|
||||||
|
# 源不存在,删除
|
||||||
|
ALTER_STMT="$ALTER_STMT
|
||||||
|
ALTER TABLE $TABLE DROP COLUMN $COL_NAME;"
|
||||||
|
CHANGES="$CHANGES\n - 删除字段:$COL_NAME"
|
||||||
|
fi
|
||||||
|
done <<< "$TARGET_FIELDS"
|
||||||
|
|
||||||
|
if [ -n "$ALTER_STMT" ]; then
|
||||||
|
echo " → 发现结构差异,生成 ALTER 语句"
|
||||||
|
|
||||||
|
# 生成脚本
|
||||||
|
cat > "$SCRIPT_FILE" << EOF
|
||||||
|
-- ============================================================
|
||||||
|
-- 表结构同步脚本:$TABLE
|
||||||
|
-- 源:准生产 ($SOURCE_HOST:$SOURCE_PORT)
|
||||||
|
-- 目标:VPS ($TARGET_HOST:$TARGET_PORT) $TARGET_DB
|
||||||
|
-- 生成时间:$(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
-- 操作:ALTER TABLE(结构差异)
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
USE $TARGET_DB;
|
||||||
|
-- 结构差异:$(echo -e "$CHANGES")
|
||||||
|
|
||||||
|
$ALTER_STMT
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 验证
|
||||||
|
-- ============================================================
|
||||||
|
-- SHOW CREATE TABLE $TABLE\\G
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 执行脚本(失败继续)
|
||||||
|
if mysql -h "$TARGET_HOST" -P "$TARGET_PORT" -u "$TARGET_USER" -p"$TARGET_PWD" -D "$TARGET_DB" < "$SCRIPT_FILE" 2>/dev/null; then
|
||||||
|
echo " ✓ ALTER TABLE 成功"
|
||||||
|
((SUCCESS_COUNT++))
|
||||||
|
TABLE_STATUS+=("| $TABLE | ALTER | ✅ | $(echo -e "$CHANGES" | wc -l) 处变更 |")
|
||||||
|
else
|
||||||
|
echo " ✗ ALTER TABLE 失败(继续执行下一个表)"
|
||||||
|
((FAIL_COUNT++))
|
||||||
|
TABLE_STATUS+=("| $TABLE | ALTER | ❌ | 执行失败 |")
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo " ✓ 结构一致,无需修改"
|
||||||
|
((SUCCESS_COUNT++))
|
||||||
|
TABLE_STATUS+=("| $TABLE | SKIP | ✅ | 结构一致 |")
|
||||||
|
rm -f "$SCRIPT_FILE" # 删除空脚本
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# 生成 README
|
||||||
|
echo ""
|
||||||
|
echo "[4/5] 生成 README.md..."
|
||||||
|
|
||||||
|
cat > "$OUTPUT_DIR/README.md" << EOF
|
||||||
|
# 表结构同步脚本 - $DATE_STR
|
||||||
|
|
||||||
|
**源:** 准生产 \`$SOURCE_HOST:$SOURCE_PORT\` \`$SOURCE_DB\`
|
||||||
|
**目标:** VPS \`$TARGET_HOST:$TARGET_PORT\` \`$TARGET_DB\`
|
||||||
|
**生成时间:** $(date '+%Y-%m-%d %H:%M:%S')
|
||||||
|
**执行状态:** $([ $FAIL_COUNT -eq 0 ] && echo "✅ 已完成" || echo "⚠️ 部分失败")
|
||||||
|
|
||||||
|
## 脚本清单
|
||||||
|
|
||||||
|
| 序号 | 脚本文件 | 操作类型 | 状态 | 说明 |
|
||||||
|
|------|---------|---------|------|------|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
i=1
|
||||||
|
for STATUS in "${TABLE_STATUS[@]}"; do
|
||||||
|
TABLE_NAME=$(echo "$STATUS" | cut -d'|' -f2 | tr -d ' ')
|
||||||
|
SCRIPT_NAME="${TARGET_DB}__${TABLE_NAME}__struct.sql"
|
||||||
|
echo "| $i | \`$SCRIPT_NAME\` $STATUS" >> "$OUTPUT_DIR/README.md"
|
||||||
|
((i++))
|
||||||
|
done
|
||||||
|
|
||||||
|
cat >> "$OUTPUT_DIR/README.md" << EOF
|
||||||
|
|
||||||
|
## 执行结果
|
||||||
|
|
||||||
|
- 总表数:$TABLE_COUNT
|
||||||
|
- 成功:$SUCCESS_COUNT
|
||||||
|
- 失败:$FAIL_COUNT
|
||||||
|
|
||||||
|
## 验证命令
|
||||||
|
|
||||||
|
\`\`\`sql
|
||||||
|
-- 检查表列表
|
||||||
|
SHOW TABLES LIKE '$TARGET_DB%';
|
||||||
|
|
||||||
|
-- 检查表结构
|
||||||
|
SHOW CREATE TABLE <table_name>\\G
|
||||||
|
\`\`\`
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 记录到记忆文件
|
||||||
|
echo ""
|
||||||
|
echo "[5/5] 记录到记忆文件..."
|
||||||
|
|
||||||
|
MEMORY_FILE="$MEMORY_DIR/$(date +%Y-%m-%d).md"
|
||||||
|
|
||||||
|
if [ ! -f "$MEMORY_FILE" ]; then
|
||||||
|
echo "# $(date +%Y-%m-%d) 工作记录" > "$MEMORY_FILE"
|
||||||
|
echo "" >> "$MEMORY_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat >> "$MEMORY_FILE" << EOF
|
||||||
|
|
||||||
|
## 表结构同步 - $(date '+%H:%M')
|
||||||
|
|
||||||
|
**源:** 准生产 \`$SOURCE_DB\`
|
||||||
|
**目标:** VPS \`$TARGET_DB\`
|
||||||
|
**处理表数:** $TABLE_COUNT
|
||||||
|
**成功:** $SUCCESS_COUNT
|
||||||
|
**失败:** $FAIL_COUNT
|
||||||
|
|
||||||
|
### 处理的表
|
||||||
|
|
||||||
|
| 表名 | 操作 | 状态 | 说明 |
|
||||||
|
|------|------|------|------|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for STATUS in "${TABLE_STATUS[@]}"; do
|
||||||
|
echo "$STATUS" >> "$MEMORY_FILE"
|
||||||
|
done
|
||||||
|
|
||||||
|
cat >> "$MEMORY_FILE" << EOF
|
||||||
|
|
||||||
|
### 脚本位置
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
$OUTPUT_DIR/
|
||||||
|
├── README.md
|
||||||
|
└── ${TARGET_DB}__*_struct.sql
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================================"
|
||||||
|
echo "执行完成!"
|
||||||
|
echo "============================================================"
|
||||||
|
echo "总表数:$TABLE_COUNT"
|
||||||
|
echo "成功:$SUCCESS_COUNT"
|
||||||
|
echo "失败:$FAIL_COUNT"
|
||||||
|
echo "脚本目录:$OUTPUT_DIR"
|
||||||
|
echo "记忆文件:$MEMORY_FILE"
|
||||||
|
echo "============================================================"
|
||||||
Reference in New Issue
Block a user