# db_utils.py import pyodbc import datetime import decimal from config import SQL_SERVER_CONN, ACCESS_DRIVER # ================= 表存在性检查 & 自动建表 ================= def table_exists(cursor, schema, table): """检查 SQL Server 表是否存在""" query = """ SELECT COUNT(*) FROM sys.tables t JOIN sys.schemas s ON t.schema_id = s.schema_id WHERE s.name = ? AND t.name = ? """ cursor.execute(query, (schema, table)) return cursor.fetchone()[0] > 0 def ensure_schema(cursor, schema): """确保 schema 存在,不存在则创建""" cursor.execute("SELECT SCHEMA_ID(?)", (schema,)) if cursor.fetchone()[0] is None: cursor.execute(f"CREATE SCHEMA [{schema}]") def _access_col_to_sql(col_info, pk_col): """将 Access 列描述 (cursor.description 元组) 转为 SQL Server 列定义 Access ODBC 驱动的 type_code 是 Python 类型对象: int → INT str → NVARCHAR(size) (size > 4000 时为 Memo 字段 → NVARCHAR(MAX)) datetime.datetime → DATETIME float → FLOAT decimal.Decimal → DECIMAL(p,s) bool → BIT """ col_name, type_code, _, size, precision, scale, nullable = col_info is_pk = (col_name == pk_col) if type_code is int: sql_type = "INT" if is_pk: sql_type += " IDENTITY(1,1) PRIMARY KEY" elif type_code is float: sql_type = "FLOAT" elif type_code is bool: sql_type = "BIT" elif type_code is datetime.datetime: sql_type = "DATETIME" elif type_code is decimal.Decimal: sql_type = f"DECIMAL({precision or 18}, {scale or 0})" elif type_code is str: # size > 4000 → Access Memo 字段,用 NVARCHAR(MAX) if not size or size <= 0 or size > 4000: sql_type = "NVARCHAR(MAX)" else: sql_type = f"NVARCHAR({size})" if is_pk: sql_type += " PRIMARY KEY" else: sql_type = "NVARCHAR(255)" if is_pk: sql_type += " PRIMARY KEY" return f"[{col_name}] {sql_type}" def create_table_from_access(sql_cursor, target_schema, target_table, acc_description, pk_col): """根据 Access cursor.description 在 SQL Server 自动建表""" col_defs = [_access_col_to_sql(col, pk_col) for col in acc_description] full_name = fmt_table(target_schema, target_table) col_str = ",\n ".join(col_defs) create_sql = f"CREATE TABLE {full_name} (\n {col_str}\n)" sql_cursor.execute(create_sql) def get_sql_conn(): """获取 SQL Server 连接""" # 显式添加 TrustServerCertificate=yes 以兼容 ODBC Driver 18+ conn_str = ( f"DRIVER={SQL_SERVER_CONN['driver']};SERVER={SQL_SERVER_CONN['server']};" f"DATABASE={SQL_SERVER_CONN['database']};UID={SQL_SERVER_CONN['uid']};PWD={SQL_SERVER_CONN['pwd']};" "Encrypt=yes;TrustServerCertificate=yes;" ) return pyodbc.connect(conn_str) def get_access_conn(file_path): """获取 Access 连接""" conn_str = f"DRIVER={ACCESS_DRIVER};DBQ={file_path};" return pyodbc.connect(conn_str) def fmt_table(schema, table): """格式化 SQL Server 表名 [schema].[table]""" return f"[{schema}].[{table}]" def get_columns(cursor, table_name): """获取 Access 表的列名""" # Access 查询表名加 [] cursor.execute(f"SELECT TOP 1 * FROM [{table_name}]") return [column[0] for column in cursor.description] def generate_insert_sql(target_schema, target_table, columns): """生成带架构的 INSERT 语句""" full_table_name = fmt_table(target_schema, target_table) col_str = ",".join([f"[{col}]" for col in columns]) placeholders = ",".join(["?"] * len(columns)) return f"INSERT INTO {full_table_name} ({col_str}) VALUES ({placeholders})"