diff --git a/migration.py b/migration.py new file mode 100644 index 0000000..220f06e --- /dev/null +++ b/migration.py @@ -0,0 +1,164 @@ +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\生产执行卡\往年生产执行卡\生产执行卡2023(1-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() \ No newline at end of file