Files
BLD_sync/migration.py

164 lines
6.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pandas as pd
import os
import urllib
from sqlalchemy import create_engine
# ==========================================
# 1. 全局配置 (Global Configuration)
# ==========================================
# 数据库连接信息
DB_CONFIG = {
"server": "192.168.110.114", # 你的服务器地址,例如: 192.168.1.100
"database": "CompanyDB", # 你的数据库名
"username": "peng", # 用户名
"password": "Cqbld123456.", # 密码
"driver": "ODBC Driver 18 for SQL Server" # 确保已安装此驱动
}
# 目标表配置
TARGET_TABLE_NAME = "customerProductType" # SQL Server 表名
TARGET_DB_SCHEMA = "warehouseOutbound" # [关键] 这里指定架构,例如 'dbo' 或 'production'
# Excel 列名映射到 SQL 字段名的逻辑键 (用于后续代码逻辑引用)
# 这里的 value 必须与 SQL 数据库中的实际字段名完全一致
SQL_COL_YEAR = "合同年份" # 数据库中存年份的字段名
SQL_COL_WORKSHOP = "车间号" # 数据库中存车间号的字段名
SQL_COL_ORDER = "工令号" # 数据库中存工令号的字段名
SQL_COL_MODEL = "客户型号" # 数据库中存客户型号的字段名
# ==========================================
# 2. 迁移任务清单 (Migration Tasks)
# ==========================================
# 可以在这里添加任意数量的文件配置
MIGRATION_TASKS = [
# --- 任务 1 ---
{
"file_path": r"\\192.168.110.113\生产执行卡\往年生产执行卡\生产执行卡2022.xlsm", # Excel文件路径
"year": 2022, # 该文件对应的合同年份
"sheet_names": ["Sheet1"], # 指定要迁移的工作表名称列表
# 映射表: Excel列名 -> SQL字段名
"mapping": {
"车间号": SQL_COL_WORKSHOP,
"工令号": SQL_COL_ORDER,
"产品型号": SQL_COL_MODEL
# 可以添加其他非关键字段...
}
},
# --- 任务 2 ---
{
"file_path": r"\\192.168.110.113\生产执行卡\往年生产执行卡\生产执行卡20231-5月.xlsm",
"year": 2023,
"sheet_names": ["Sheet1"], # 只迁移 "汇总" 表
"mapping": {
"车间号": SQL_COL_WORKSHOP,
"工令号": SQL_COL_ORDER,
"产品型号": SQL_COL_MODEL
}
}
]
# ==========================================
# 3. 核心逻辑
# ==========================================
def get_db_engine():
params = urllib.parse.quote_plus(
f"DRIVER={{{DB_CONFIG['driver']}}};"
f"SERVER={DB_CONFIG['server']};"
f"DATABASE={DB_CONFIG['database']};"
f"UID={DB_CONFIG['username']};"
f"PWD={DB_CONFIG['password']};"
f"TrustServerCertificate=yes;"
)
# 使用 fast_executemany 提高写入速度
return create_engine(f"mssql+pyodbc:///?odbc_connect={params}", fast_executemany=True)
def run_migration():
engine = get_db_engine()
print(f"连接数据库... [{TARGET_DB_SCHEMA}].[{TARGET_TABLE_NAME}]")
for task in MIGRATION_TASKS:
file_path = task['file_path']
year_val = task['year']
# mapping 的键(Key)是Excel列名值(Value)是SQL列名
mapping = task['mapping']
if not os.path.exists(file_path):
print(f"文件不存在: {file_path}")
continue
print(f"\n-------- 处理文件: {os.path.basename(file_path)} --------")
try:
# 读取 Excel
xls_dict = pd.read_excel(file_path, sheet_name=task['sheet_names'])
if not isinstance(xls_dict, dict):
first_sheet = task['sheet_names'][0] if task['sheet_names'] else "Sheet1"
xls_dict = {first_sheet: xls_dict}
for sheet_name, df in xls_dict.items():
if df.empty: continue
# 1. 清洗表头:去除列名前后的空格 (防止 "车间 " 匹配不上 "车间")
df.columns = df.columns.astype(str).str.strip()
# 2. 【关键步骤】只筛选指定的源字段
# 我们只提取 mapping 字典中 key 定义的列
source_cols = list(mapping.keys())
# 检查 Excel 里是否缺列
missing_source = [c for c in source_cols if c not in df.columns]
if missing_source:
print(f" [跳过] 工作表 {sheet_name} 缺少源列: {missing_source}")
continue
# 3. 提取数据并重命名
# 先提取 -> 只有这几列
df_subset = df[source_cols].copy()
# 后重命名 -> 变成数据库的列名
df_subset.rename(columns=mapping, inplace=True)
# 4. 注入年份字段
df_subset[SQL_COL_YEAR] = year_val
# 此时 df_subset 的列名应该完全等于SQL字段列表
# 5. 数据清洗
# 确保关键字段非空
subset_keys = [SQL_COL_YEAR, SQL_COL_WORKSHOP, SQL_COL_ORDER]
df_subset.dropna(subset=subset_keys, inplace=True)
# 确保唯一性
df_subset.drop_duplicates(subset=subset_keys, keep='first', inplace=True)
if df_subset.empty:
print(f" -> 工作表 {sheet_name} 清洗后无数据")
continue
print(f" -> 工作表 {sheet_name}: 准备写入 {len(df_subset)} 行...")
# 6. 写入数据库
try:
# 使用 engine.connect() 显式连接
with engine.connect() as conn:
df_subset.to_sql(
name=TARGET_TABLE_NAME,
schema=TARGET_DB_SCHEMA,
con=conn,
if_exists='append', # 追加模式
index=False,
chunksize=1000
)
print(" -> [成功] 写入完成")
except Exception as e:
print(f" -> [写入错误] {e}")
# 如果报错,打印一下列名帮助排查
print(f" 当前DataFrame列名: {df_subset.columns.tolist()}")
except Exception as e:
print(f" -> [文件处理异常] {e}")
if __name__ == "__main__":
run_migration()