你遇到的错误:
```
ERROR 1064 (42000) at line 4: You have an error in your SQL syntax; ... near '% THEN a.sumnetpremium ELSE 0 END) / 10000, 2) AS xccs,
ROUND(SUM(CASE WHEN '
```
这是一个典型的 **SQL 语法解析错误**,关键线索是 `near '%'` —— MySQL 在遇到 `%` 符号时无法继续解析,说明这个 `%` 没有被正确转义或出现在不该出现的位置。
---
## ✅ 错误原因分析
### ❌ 根本问题:**SQL 文件中使用了未转义的 `%` 字符**
在 Bash 脚本中通过 `mysql -e "$(cat sql_file)"` 执行 SQL 时:
- 如果你的 `.sql` 文件里包含 `%` 字符(比如在 `LIKE` 查询、条件判断中的百分比逻辑、字符串格式化残留等),Bash 或 `mysql` 命令行客户端会将其误解为 **格式占位符**。
- 特别是在 `CASE WHEN ... LIKE '%XX%'` 这类语句中,如果没有正确处理,就会导致语法断裂。
#### 🔍 示例出错 SQL 片段(假设存在于 `${SQL_FILE}` 中):
```sql
SELECT
...
ROUND(SUM(CASE WHEN comcode LIKE '%A%' THEN a.sumnetpremium ELSE 0 END) / 10000, 2) AS xccs,
...
FROM ...
```
当 Bash 将这段内容插入到 `-e "..."` 的双引号字符串中时,`%` 可能被解释器提前处理(尤其某些环境下与 `printf` 风格解析冲突),导致传给 MySQL 的 SQL 已经损坏。
---
## ✅ 解决方案
### ✅ 方案一:【推荐】改用 `mysql < file.sql` 方式执行,避免内联拼接
不要用 `-e "$(cat ...)"`,而是把整个 SQL 当作标准输入传递,完全避开 shell 对特殊字符的插值问题。
#### 修改原脚本中的执行部分为:
```bash
# 原来的危险方式(删除)
# mysql --host=... -e "REPLACE INTO ... $(cat ${SQL_FILE})"
# 改成安全的方式:通过管道或重定向执行
log "开始执行SQL文件..."
{
echo "REPLACE INTO ${TARGET_DB}.${TARGET_TABLE} (
comcode, comname, qkqjbf, jycjbf, xzjgjqx, xzjgsyx, cljgxc, cljgjc,
qbyfdy, qbyffdy, jycxzjgjqx, jycxzjgsyx, xcqb, xccs, xcxlc, xcqt,
jcqb, jcxt, jccs, jcxlc, jchlw, jcqt, jycdyxc, jycdyjc,
jycfdyxc, jycfdyjc, fyykdyxc, fyykdyjc, fyykfdyxc, fyykfdyjc, yyk, fyh, yyh, tzc, bfqt, jyghc,
operatedate, refreshtime
)"
cat "${SQL_FILE}"
} | mysql --host=${TARGET_HOST} --port=${TARGET_PORT} \
--user=${TARGET_USER} --password=${TARGET_PASS} \
--default-character-set=utf8mb4 \
--local-infile=1 2>> ${LOG_FILE}
```
> 📝 注意:我们使用 `{ echo "..."; cat ...; } | mysql` 把 `REPLACE INTO (...)` 和 SQL 文件内容合并后作为完整 SQL 输入。
---
### ✅ 方案二:确保 `.sql` 文件中 `%` 正确使用(如必须保留 `-e`)
如果你坚持使用 `-e` 模式,则需保证:
- 不要在 SQL 中直接写 `%`,而应写成 `\%`(但在 MySQL 中 `\%` 并不总是有效!)
- 更稳妥的是避免使用 `%` 在字符串中 → 改用变量绑定?但 bash 不支持。
👉 实际上,**这是不可靠的**。因此强烈建议放弃 `-e` + `$(cat)` 的方式。
---
### ✅ 方案三:预检查 `.sql` 文件是否含非法结构
添加一个检查步骤:
```bash
log "校验 SQL 文件是否存在潜在问题字符..."
if grep -q "%" "${SQL_FILE}" && ! grep -q "LIKE.*%" "${SQL_FILE}"; then
log "警告:检测到孤立 % 符号,可能导致语法错误,请确认是否用于 CASE/算术表达式?"
fi
# 更进一步:尝试语法解析(需要 mysqldumpslow 或外部工具辅助)
```
不过最根本的解决办法还是换执行方式。
---
### ✅ 方案四:启用 `--force` 忽略部分错误(仅调试用)
```bash
mysql ... --force < input.sql
```
但这只会跳过错误,不会修复问题,**不推荐生产环境使用**。
---
## ✅ 完整修正后的核心执行代码(推荐版本)
```bash
log "==================================== 同步开始 ===================================="
log "SQL文件路径:${SQL_FILE}"
log "目标表:${TARGET_DB}.${TARGET_TABLE}"
log "开始执行SQL文件..."
# 使用安全方式执行:组合 SQL 并通过 stdin 输入
{
echo "REPLACE INTO ${TARGET_DB}.${TARGET_TABLE} (
comcode, comname, qkqjbf, jycjbf, xzjgjqx, xzjgsyx, cljgxc, cljgjc,
qbyfdy, qbyffdy, jycxzjgjqx, jycxzjgsyx, xcqb, xccs, xcxlc, xcqt,
jcqb, jcxt, jccs, jcxlc, jchlw, jcqt, jycdyxc, jycdyjc,
jycfdyxc, jycfdyjc, fyykdyxc, fyykdyjc, fyykfdyxc, fyykfdyjc, yyk, fyh, yyh, tzc, bfqt, jyghc,
operatedate, refreshtime
)"
cat "${SQL_FILE}"
} | mysql --host=${TARGET_HOST} --port=${TARGET_PORT} \
--user=${TARGET_USER} --password=${TARGET_PASS} \
--default-character-set=utf8mb4 \
--local-infile=1 2>> ${LOG_FILE}
EXECUTION_RESULT=$?
```
然后继续原来的 `$?` 判断即可。
---
## ✅ 补充建议
| 建议 | 说明 |
|------|------|
| 🔐 使用配置文件代替明文密码 | 如 `~/.my.cnf` 避免命令行泄露密码 |
| 🧪 测试 SQL 是否可独立运行 | `mysql < test.sql` 看是否报错 |
| 📜 日志记录实际执行的 SQL(脱敏) | 便于排查 |
---
## ✅ 总结
> ❌ 错误根源:`%` 字符在 Bash 插值过程中破坏了 SQL 结构
> ✅ 正确做法:**不要用 `mysql -e "$(cat ...)"` 执行复杂 SQL**,改用 `echo + cat | mysql` 的管道模式
> ✅ 最佳实践:将 SQL 构造成完整语句并通过 stdin 输入,彻底规避 shell 转义问题
---
###