You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python执行SQL查询时批量排除CSV值,避免大量占位符

问题描述

我想用Python实现自动从SQL查询中排除CSV文件内的数百个值,目前已将CSV中的值转换为列表,通过cursor.execute(statement, exclusion())执行.sql文件中的查询语句,其中exclusion()返回该列表。但当前需在SQL语句的NOT IN子句中为列表每个值添加一个?占位符,而该列表每周都会更新,手动维护这些占位符非常繁琐,希望找到无需手动添加大量占位符的解决方法。

以下是我使用的完整代码:

# 将CSV中的值转换为列表
def exclusion():
    repeat_exclusion_df = pd.read_csv('repeat_names.csv')
    names_list = list(repeat_exclusion_df['names'])
    return names_list


# 读取.sql文件并转换为pandas DataFrame
import pandas as pd
import logging

cursor = connection_sql(uid=arguments.company_uid,
                        pwd=arguments.company_pwd,
                        server=arguments.company_server,
                        db=arguments.company_db)
with open('names_query.sql') as sql_file:
    logging.info('Open sql file')
    df_list = []
    for statement in sql_file.read().split(';'):
        db_cursor = cursor.execute(statement, exclusion())
        logging.info('Read each sql query')
        if db_cursor is not None:
            results = db_cursor.fetchall()
            if results:
                df = pd.DataFrame(results)
                df.columns = results[0].keys()
                df_list.append(df)

.sql文件中的查询示例:

SELECT NAME
FROM [DATABASE]
WHERE NAME NOT IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
解决方案

方法1:动态生成占位符字符串

直接在Python中根据排除列表的长度,自动生成对应数量的?占位符,替换SQL语句中固定的占位符块。

修改后的代码示例:

def exclusion():
    repeat_exclusion_df = pd.read_csv('repeat_names.csv')
    names_list = list(repeat_exclusion_df['names'])
    return names_list

import pandas as pd
import logging

# 获取排除列表
exclude_names = exclusion()
# 生成对应数量的占位符
placeholders = ', '.join(['?'] * len(exclude_names))

cursor = connection_sql(uid=arguments.company_uid,
                        pwd=arguments.company_pwd,
                        server=arguments.company_server,
                        db=arguments.company_db)
with open('names_query.sql') as sql_file:
    logging.info('Open sql file')
    df_list = []
    # 读取SQL内容,建议直接在sql文件中用标记占位,比如WHERE NAME NOT IN ({PLACEHOLDERS})
    sql_content = sql_file.read().replace('{PLACEHOLDERS}', placeholders)
    
    for statement in sql_content.split(';'):
        # 跳过空语句
        if not statement.strip():
            continue
        db_cursor = cursor.execute(statement, exclude_names)
        logging.info('Execute sql query')
        if db_cursor is not None:
            results = db_cursor.fetchall()
            if results:
                df = pd.DataFrame(results)
                df.columns = results[0].keys()
                df_list.append(df)

优化说明:把.sql文件中的NOT IN子句改成WHERE NAME NOT IN ({PLACEHOLDERS}),替换时更精准,避免正则匹配的复杂度。

方法2:使用临时表批量插入排除值

把CSV中的排除值插入到数据库的临时表中,然后在SQL查询中直接关联临时表,彻底摆脱占位符的限制。

代码示例:

def exclusion():
    repeat_exclusion_df = pd.read_csv('repeat_names.csv')
    names_list = list(repeat_exclusion_df['names'])
    return names_list

import pandas as pd
import logging

exclude_names = exclusion()
cursor = connection_sql(uid=arguments.company_uid,
                        pwd=arguments.company_pwd,
                        server=arguments.company_server,
                        db=arguments.company_db)

# 创建临时表
cursor.execute("CREATE TABLE #temp_exclusion (name NVARCHAR(255))")
# 批量插入排除值
insert_query = "INSERT INTO #temp_exclusion (name) VALUES (?)"
cursor.executemany(insert_query, [(name,) for name in exclude_names])

with open('names_query.sql') as sql_file:
    logging.info('Open sql file')
    df_list = []
    # 修改SQL语句,直接查询临时表
    sql_content = sql_file.read().replace(
        "WHERE NAME NOT IN (?,?,?,?)", 
        "WHERE NAME NOT IN (SELECT name FROM #temp_exclusion)"
    )
    for statement in sql_content.split(';'):
        if not statement.strip():
            continue
        db_cursor = cursor.execute(statement)
        logging.info('Execute sql query')
        if db_cursor is not None:
            results = db_cursor.fetchall()
            if results:
                df = pd.DataFrame(results)
                df.columns = results[0].keys()
                df_list.append(df)

# 临时表会在连接关闭后自动销毁,也可手动删除
cursor.execute("DROP TABLE #temp_exclusion")

方法3:使用表值参数(仅适用于SQL Server)

如果你的数据库是SQL Server,可以定义自定义表类型,将Python中的列表作为表值参数传入SQL,这种方式性能更优,适合大量数据的场景。

步骤1:在SQL Server中创建表值类型:

CREATE TYPE NameListType AS TABLE (Name NVARCHAR(255))

步骤2:Python代码修改:

import pyodbc
import pandas as pd
import logging

def exclusion():
    repeat_exclusion_df = pd.read_csv('repeat_names.csv')
    return repeat_exclusion_df['names'].tolist()

exclude_names = exclusion()
# 创建支持表值参数的连接
conn_str = f"DRIVER={{ODBC Driver 17 for SQL Server}};SERVER={arguments.company_server};DATABASE={arguments.company_db};UID={arguments.company_uid};PWD={arguments.company_pwd}"
conn = pyodbc.connect(conn_str)
cursor = conn.cursor()

# 将列表转换为表值参数格式
tvp = [(name,) for name in exclude_names]

with open('names_query.sql') as sql_file:
    # 修改SQL语句,直接传入表值参数
    sql_content = sql_file.read().replace(
        "WHERE NAME NOT IN (?,?,?,?)",
        "WHERE NAME NOT IN (SELECT Name FROM ?)"
    )
    # 传入表值参数执行查询
    db_cursor = cursor.execute(sql_content, (tvp,))
    results = db_cursor.fetchall()
    if results:
        df = pd.DataFrame(results)
        df.columns = results[0].keys()
        df_list = [df]

conn.commit()
conn.close()

内容的提问来源于stack exchange,提问作者Patrick

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 08:50:25