这个错误是因为在使用 `executemany` 批量更新数据库时,参数格式不正确。以下是修正后的完整脚本和解决方案:
---
### **错误分析与修复**
#### **问题原因**
1. `executemany` 要求参数必须是字典列表(当使用命名参数时)或元组列表(当使用位置参数时)
2. 当前代码混合使用了两种风格
#### **关键修复**
1. 统一使用元组列表格式
2. 改进数据库更新逻辑
---
### **修正后的完整脚本**
```python
import os
import pandas as pd
from sqlalchemy import create_engine
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# 数据库配置
DB_CONFIG = {
'host': 'your_host',
'port': 3306,
'user': 'your_user',
'password': 'your_password',
'database': 'your_db',
'charset': 'latin1'
}
def get_script_directory():
"""获取脚本所在目录"""
try:
return os.path.dirname(os.path.abspath(__file__))
except NameError:
return os.getcwd()
def count_xlsx_files():
"""统计xlsx文件"""
script_dir = get_script_directory()
os.chdir(script_dir)
logger.info(f"当前工作目录: {os.getcwd()}")
logger.info(f"目录内容: {os.listdir('.')}")
file_stats = {}
for filename in os.listdir('.'):
if filename.startswith('~$') or not filename.lower().endswith('.xlsx'):
continue
filepath = os.path.join(script_dir, filename)
try:
logger.info(f"正在处理文件: {filename}")
with pd.ExcelFile(filepath, engine='openpyxl') as xls:
for sheet_name in xls.sheet_names:
df = pd.read_excel(xls, sheet_name=sheet_name)
# 检查必要列是否存在
if not all(col in df.columns for col in ['sub_name', 'dist_id']):
logger.warning(f"文件 {filename} 缺少必要列")
continue
for _, row in df.iterrows():
sub_name = str(row['sub_name']).strip()
dist_id = str(row['dist_id']).strip()
if sub_name and dist_id:
key = (sub_name, dist_id)
file_stats[key] = file_stats.get(key, 0) + 1
except Exception as e:
logger.error(f"处理文件 {filename} 失败: {str(e)}")
continue
return file_stats
def update_database(file_stats):
"""更新数据库(修正批量操作)"""
try:
engine = create_engine(
f"mysql+pymysql://{DB_CONFIG['user']}:{DB_CONFIG['password']}"
f"@{DB_CONFIG['host']}:{DB_CONFIG['port']}/{DB_CONFIG['database']}",
connect_args={'charset': 'latin1'}
)
current_date = datetime.now().strftime('%Y-%m-%d')
updates = []
for (sub_name, dist_id), count in file_stats.items():
# 转换为Latin1兼容格式
safe_sub = sub_name.encode('gbk').decode('latin1', errors='ignore')
safe_dist = dist_id.encode('gbk').decode('latin1', errors='ignore')
# 使用元组格式(位置参数)
updates.append((current_date, safe_sub, safe_dist, count))
with engine.begin() as conn:
# 使用位置参数风格的批量操作
conn.execute(
"""INSERT INTO file_stats
(collection_date, sub_name, dist_id, count)
VALUES (%s, %s, %s, %s)
ON DUPLICATE KEY UPDATE count = VALUES(count)""",
updates
)
except Exception as e:
logger.error(f"数据库操作失败: {str(e)}", exc_info=True)
raise
if __name__ == "__main__":
try:
logger.info("====== 开始执行统计 ======")
stats = count_xlsx_files()
if not stats:
logger.warning("未找到有效数据,请检查:")
logger.warning("1. Excel文件是否包含sub_name和dist_id列")
logger.warning("2. 数据是否非空")
else:
logger.info(f"共统计到 {len(stats)} 条有效数据")
update_database(stats)
logger.info("数据库更新成功")
except Exception as e:
logger.error(f"程序运行异常: {str(e)}", exc_info=True)
finally:
logger.info("====== 执行结束 ======")
```
---
### **关键改进点**
1. **数据库批量操作修正**
```python
# 原错误代码(混合风格)
# 修正为统一使用位置参数风格的元组列表
updates.append((current_date, safe_sub, safe_dist, count))
```
2. **增强健壮性**
- 检查必要列是否存在:
```python
if not all(col in df.columns for col in ['sub_name', 'dist_id']):
logger.warning(f"文件 {filename} 缺少必要列")
```
3. **改进错误日志**
- 添加 `exc_info=True` 显示完整堆栈:
```python
logger.error(f"数据库操作失败: {str(e)}", exc_info=True)
```
4. **编码处理优化**
- 更安全的GBK→Latin1转换:
```python
.decode('latin1', errors='ignore')
```
---
### **验证测试**
1. **测试数据准备**
```python
# 测试用Excel文件应包含:
# | sub_name | dist_id |
# |----------|---------|
# | 北京 | A001 |
# | 上海 | B002 |
```
2. **预期输出**
```
2025-08-07 15:30:00,014 - INFO - ====== 开始执行统计 ======
2025-08-07 15:30:00,020 - INFO - 正在处理文件: test.xlsx
2025-08-07 15:30:01,290 - INFO - 共统计到 2 条有效数据
2025-08-07 15:30:01,845 - INFO - 数据库更新成功
2025-08-07 15:30:01,846 - INFO - ====== 执行结束 ======
```
3. **数据库验证**
```sql
SELECT
collection_date,
CONVERT(CONVERT(sub_name USING latin1) USING gbk) AS sub_name,
CONVERT(CONVERT(dist_id USING latin1) USING gbk) AS dist_id,
count
FROM file_stats;
```
---
### **常见问题处理**
**Q1: 仍然出现编码错误怎么办?**
- 尝试修改编码处理方式:
```python
# 替代方案:UTF-8 → Latin1
safe_sub = sub_name.encode('utf-8').decode('latin1', errors='ignore')
```
**Q2: 如何提高大文件处理速度?**
- 使用分块读取:
```python
for chunk in pd.read_excel(xls, sheet_name=sheet_name, chunksize=1000):
# 处理每个chunk
```
**Q3: 需要记录处理过的文件怎么办?**
- 添加状态记录:
```python
processed_files = set()
if filename in processed_files:
continue
# 处理完成后
processed_files.add(filename)
```