你遇到的错误是:
```
[ERROR] 同步表 xxx 时发生异常: 1054 (42S22): Unknown column 'I' in 'field list'
```
这个错误非常关键,说明 **MySQL 在解析 SQL 语句时,把某个字段名误识别成了 `'I'`(一个字符串)而不是列名**。
---
## 🔍 错误根源分析
### ❌ 根本原因:
你在构建插入语句时,使用了如下方式获取列名:
```python
columns = [desc[0] for desc in rows[0].keys()]
```
但 `sqlite3.Row.keys()` 返回的是 **字符串本身带有特殊字符或格式问题**,比如:
- 列名为:`"Image"` → 正常
- 但某些情况下 SQLite 表中列名可能是:`[Image]`、`"Image"`、甚至包含换行或不可见字符
- 或者更严重的情况:**某列的名字其实是 `'I'` 字符串本身?不可能!**
但实际上最可能的原因是:
> ⚠️ **你的数据中某个字段的值是 `'I'`,而你错误地将它当作了列名!**
但这也不对……我们再看日志:
```
📥 从 AppSortingTaskImages 读取到 1790 条数据
[ERROR] Unknown column 'I' in 'field list'
```
这说明不是“值”是 `'I'`,而是 **SQL 语句中出现了 `INSERT INTO ... VALUES ('I', ...)` 被当作字段列表解析了?**
不,真正的罪魁祸首在这一行代码:
```python
insert_columns = ', '.join([f"`{col}`" for col in all_columns_with_lineno])
```
和
```python
placeholders = ', '.join(['%s'] * len(all_columns_with_lineno))
```
如果 `all_columns_with_lineno` 中有一个元素是字符串 `'I'`,那就会变成:
```sql
INSERT INTO table (`I`, `other_col`) VALUES (%s, %s)
```
→ MySQL 会认为你要插入一个叫 `I` 的字段!
所以问题来了:**为什么列名里会出现 `'I'`?**
---
# ✅ 真正原因:SQLite 查询结果的 `row.keys()` 出现了异常列名!
我们来加一个调试打印:
## ✅ 修改建议:添加列名检查
在 `sync_table_from_sqlite_to_mysql` 中找到这段:
```python
# 获取列名(来自 SQLite)
columns = [desc[0] for desc in rows[0].keys()]
```
替换成:
```python
# === DEBUG: 打印原始 keys ===
raw_keys = rows[0].keys()
print(f"🔍 原始列名列表: {raw_keys}")
# 检查是否有非法列名
for k in raw_keys:
if not isinstance(k, str):
print(f"[WARN] 非字符串列名: {k} (类型: {type(k)})")
if k.strip() == 'I':
print(f"[CRITICAL] 发现危险列名: '{k}' ← 这会导致 SQL 错误!")
if not k.isidentifier():
print(f"[WARN] 非法标识符列名: '{k}'")
columns = [k for k in raw_keys]
all_columns_with_lineno = columns + ['LineNumber']
```
运行后你会看到输出类似:
```
🔍 原始列名列表: ['Id', 'TaskId', 'ImagePath', 'I']
```
或者:
```
🔍 原始列名列表: ['Id', 'TaskId', '\ufeffImagePath'] # BOM 头导致异常
```
但最大可能是:
> 💥 你的 SQLite 表中真的存在一个字段名叫 `'I'`!
---
## 🧪 示例验证
假设 SQLite 中有这样一个表结构:
| Id | TaskId | I | ImagePath |
|----|--------|-------|-----------|
| 1 | 100 | YES | /img/1.jpg |
然后你执行:
```python
columns = list(rows[0].keys()) # -> ['Id', 'TaskId', 'I', 'ImagePath']
```
然后生成 SQL:
```sql
INSERT INTO AI_AppSortingTaskImages (`Id`, `TaskId`, `I`, `ImagePath`, `LineNumber`)
VALUES (%s, %s, %s, %s, %s)
```
✅ 看起来没问题?
但是!如果你的 MySQL 目标表 **没有 `I` 这个字段**,就会报错:
```
Unknown column 'I' in 'field list'
```
---
## ✅ 解决方案
### ✅ 方案一:确认目标 MySQL 表结构是否一致
#### 1. 查看 SQLite 表结构
```python
def print_sqlite_schema(db_path, table_name):
conn, temp = connect_sqlite_remote_safely(db_path)
if not conn:
return
try:
cursor = conn.cursor()
cursor.execute(f"PRAGMA table_info({table_name})")
print(f"📊 SQLite 表 {table_name} 结构:")
for row in cursor.fetchall():
print(row) # cid, name, type, notnull, dflt_value, pk
finally:
close_sqlite_connection(conn, temp)
```
调用它测试:
```python
print_sqlite_schema(r'Y:\sorting.db', 'AppSortingTaskImages')
```
输出示例:
```
(0, 'Id', 'INTEGER', 1, None, 1)
(1, 'TaskId', 'TEXT', 0, None, 0)
(2, 'I', 'TEXT', 0, None, 0) ← 看到了吗?真有个叫 'I' 的字段!
(3, 'ImagePath', 'TEXT', 0, None, 0)
```
#### 2. 查看 MySQL 表结构
```sql
DESCRIBE AI_AppSortingTaskImages;
```
看看有没有 `I` 字段。如果没有,就补上:
```sql
ALTER TABLE AI_AppSortingTaskImages ADD COLUMN `I` TEXT DEFAULT NULL;
```
---
### ✅ 方案二:过滤掉你不想要的字段(推荐)
如果你不想同步 `I` 字段,那就手动指定白名单列名:
```python
# 在 sync_table_from_sqlite_to_mysql 函数内
WHITELIST_COLUMNS = {
'AppSortingTasks': ['Id', 'TaskId', 'Status', 'CreationTime'],
'AppOperationLogs': ['Id', 'LogType', 'Message', 'CreationTime'],
'AppSortingTaskImages': ['Id', 'TaskId', 'ImagePath', 'ThumbnailPath'],
# 其他表...
}
# 替换原来的 columns 获取方式
if table_name in WHITELIST_COLUMNS:
columns = WHITELIST_COLUMNS[table_name]
else:
columns = list(rows[0].keys()) # 默认行为
```
这样就能跳过奇怪的字段如 `'I'`。
---
### ✅ 方案三:重命名可疑字段(临时修复)
```python
def sanitize_column_name(name):
"""清理列名中的非法字符"""
name = name.strip()
if name == 'I':
return 'Flag_I' # 重命名为安全名称
if name.startswith('[') and name.endswith(']'):
return name[1:-1]
return name
columns = [sanitize_column_name(k) for k in raw_keys]
```
然后确保 MySQL 表中有对应字段 `Flag_I`。
---
## ✅ 总结问题本质
| 项目 | 内容 |
|------|------|
| ❌ 报错原因 | MySQL 插入语句引用了一个不存在的字段 `'I'` |
| 🤔 为何出现 `'I'` | SQLite 表中确实存在名为 `I` 的字段 |
| 🛠️ 解决方法 | ① 添加该字段到 MySQL;② 过滤掉它;③ 重命名 |
| ✅ 推荐做法 | 使用白名单控制同步字段,避免动态反射风险 |
---
## ✅ 建议最终修改(立即可用)
```python
# 在文件顶部定义白名单
WHITELIST_COLUMNS = {
"AppSortingTasks": ["Id", "TaskId", "Status", "Priority", "CreationTime", "CompletionTime", "Operator", "LineId"],
"AppOperationLogs": ["Id", "LogType", "Message", "CreationTime", "TaskId", "BoxCode"],
"AppReportRecords": ["Id", "ReportDate", "TotalTasks", "SuccessCount", "ErrorCount", "CreationTime"],
"AppSortingTaskItems": ["Id", "TaskId", "SkuCode", "TargetBin", "ActualBin", "Status", "ScanTime", "CreationTime"],
"AppSortingTaskImages": ["Id", "TaskId", "ImagePath", "ThumbnailPath", "UploadStatus", "CreationTime"],
"AppSortingTaskSkus": ["Id", "TaskId", "SkuCode", "Quantity", "PickedQuantity", "CreationTime"],
"AppSortingTaskBoxes": ["Id", "TaskId", "BoxCode", "Status", "SealTime", "CreationTime"],
}
```
然后在同步函数中替换列名获取逻辑:
```python
# 获取列名:优先使用白名单,否则自动探测
if table_name in WHITELIST_COLUMNS:
columns = WHITELIST_COLUMNS[table_name]
print(f" ✅ 使用白名单列: {columns}")
else:
columns = [k for k in rows[0].keys()]
print(f" ⚠️ 自动探测列名: {columns}")
```
---
###