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

如何高效替代iterrows遍历DataFrame执行SQL查询并合并结果?

问题描述

我有如下Pandas DataFrame:

import pandas as pd
import sqlalchemy
con = sqlalchemy.create_engine('....')

df=pd.DataFrame({'user_id':[1,2,3],'start_date':pd.Series(['2022-05-01 00:00:00','2022-05-10 00:00:00','2022-05-20 00:00:00'],dtype='datetime64[ns]'),
                 'end_date':pd.Series(['2022-06-01 00:00:00','2022-06-10 00:00:00','2022-06-20 00:00:00'],dtype='datetime64[ns]')})

'''
user_id    start_date            end_date
1          2022-05-01 00:00:00   2022-06-01 00:00:00
2          2022-05-10 00:00:00   2022-06-10 00:00:00
3          2022-05-20 00:00:00   2022-06-20 00:00:00
'''

我需要从数据库中获取每个用户在df指定日期范围内的销售数据,当前使用iterrows()循环查询的代码可以正常运行,但速度极慢:

df_stats=pd.DataFrame()
for k,j in df.iterrows():
    sql='''
  select '{}' as user_id,sum(item_price) as sales,count(return) as return from sales 
  where created_at between '{}' and '{}' and user_id={}'''.format(j['user_id'],j['start_date'],j['end_date'],j['user_id'])
    sql_to_df = pd.read_sql(sql, con)
    df_stats = df_stats.append(sql_to_df)

final=df.merge(df_stats,on='user_id')

'''
final:

user_id    start_date            end_date              sales   return
1          2022-05-01 00:00:00   2022-06-01 00:00:00   1500    5
2          2022-05-10 00:00:00   2022-06-10 00:00:00   2900    9
3          2022-05-20 00:00:00   2022-06-20 00:00:00   1450    1
'''

请问有没有更高效的实现方式?

高效实现方案

方法1:利用数据库临时表做关联聚合(最优方案)

把本地DataFrame的用户日期条件写入数据库临时表,直接在数据库层面完成关联和聚合,只需要一次查询,效率远高于循环。

代码示例(以PostgreSQL为例,临时表会话结束后自动销毁):

# 将本地df写入数据库临时表
df.to_sql('user_date_ranges', con, index=False, if_exists='replace', method='multi')

# 编写关联查询SQL,直接计算统计结果
sql = '''
SELECT 
    u.user_id,
    SUM(s.item_price) AS sales,
    COUNT(s.return) AS return
FROM user_date_ranges u
JOIN sales s 
    ON s.user_id = u.user_id 
    AND s.created_at BETWEEN u.start_date AND u.end_date
GROUP BY u.user_id
'''

# 一次性读取统计数据
df_stats = pd.read_sql(sql, con)

# 合并原数据与统计结果
final = df.merge(df_stats, on='user_id', how='left')

优势:借助数据库优化器处理关联逻辑,避免多次网络请求,同时彻底规避SQL注入风险。

方法2:构造参数化批量查询(无临时表时可用)

如果无法创建临时表,可以构造包含所有用户条件的参数化SQL,一次性查询所有数据:

# 提取用户参数列表
params = list(df[['user_id', 'start_date', 'end_date']].itertuples(index=False, name=None))

# 构造批量查询条件
conditions = " OR ".join([
    "(s.user_id = %s AND s.created_at BETWEEN %s AND %s)" 
    for _ in params
])

# 完整SQL语句
sql = f'''
SELECT 
    s.user_id,
    SUM(s.item_price) AS sales,
    COUNT(s.return) AS return
FROM sales s
WHERE {conditions}
GROUP BY s.user_id
'''

# 执行参数化查询
df_stats = pd.read_sql(sql, con, params=[p for param_tuple in params for p in param_tuple])

# 合并数据
final = df.merge(df_stats, on='user_id', how='left')

注意:如果用户数量过多(如上万条),会导致SQL语句过长,此时优先选择临时表方案。

方法3:用apply替代iterrows(次优选择)

如果必须用Pandas行处理,用apply替代iterrows,配合参数化查询提升效率:

def get_user_stats(row):
    sql = '''
    SELECT 
        %(user_id)s AS user_id,
        SUM(item_price) AS sales,
        COUNT(return) AS return
    FROM sales
    WHERE created_at BETWEEN %(start_date)s AND %(end_date)s 
      AND user_id = %(user_id)s
    '''
    return pd.read_sql(sql, con, params=row.to_dict())

# 批量处理并合并结果
df_stats = pd.concat(df.apply(get_user_stats, axis=1).tolist(), ignore_index=True)

final = df.merge(df_stats, on='user_id')

优势:比iterrows效率略高,且参数化查询更安全,但仍不如一次性数据库查询高效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:40:49