基于SQL查询为DataFrame新增关联原始订单日期列的问题
解决DataFrame新增原订单日期列的问题
问题分析
你的代码存在几个关键问题:
- 原始SQL查询有语法错误:
select OrderID, OrderDate, OrigOrderID, from tbl_Orders末尾多了一个逗号,会导致SQL执行失败 - 使用
iterrows循环+pandasql的方式效率极低,且pandasql默认查询的是内存中的DataFrame而非远程数据库,直接拼接SQL语句还存在注入风险 - 当
OrigOrderID为NULL时,查询会返回空结果,且返回的是DataFrame对象而非单个日期字符串,导致新列存储的是DataFrame而非预期的日期值
最优解决方案:数据库层面直接关联查询
直接修改SQL语句,通过自关联获取原订单的日期,一次性把数据查出来,这是效率最高的方式:
import pyodbc import pandas as pd conx_string = "driver={SQL SERVER}; server=mssql_db; database=db; UID=usr; PWD=my_pwd;" conn = pyodbc.connect(conx_string) # 自关联表获取原订单日期,直接返回包含目标列的结果 query = """ select o.OrderID, o.OrderDate, o.OrigOrderID, orig.OrderDate as [Date of receipt of OrigOrder] from tbl_Orders o left join tbl_Orders orig on o.OrigOrderID = orig.OrderID """ # 用pandas内置方法直接读取查询结果,无需手动处理游标 df = pd.read_sql(query, conn) # 查看最终结果 print(df)
该方案返回的DataFrame会直接包含新增列,OrigOrderID为NULL的行对应列值也为NULL,完全符合预期。
备选方案:本地DataFrame关联
如果已经把数据拉到本地,不想再发起数据库查询,可以用pandas的merge方法实现关联:
# 假设已经有初始的df数据 # 先创建原订单的映射表:OrigOrderID -> OrderDate orig_order_map = df[['OrderID', 'OrderDate']].rename( columns={'OrderID': 'OrigOrderID', 'OrderDate': 'Date of receipt of OrigOrder'} ) # 左关联原表和映射表,获取对应日期 df = df.merge(orig_order_map, on='OrigOrderID', how='left') print(df)
这种方式比逐行循环查询高效得多,避免了大量IO操作的性能损耗。
原有代码的修正(不推荐,仅作参考)
如果一定要沿用你最初的思路,需要修正以下几点:
- 修复SQL语法错误
- 使用参数化查询避免注入风险,直接查询数据库而非内存DataFrame
- 提取查询结果的单个值,处理
NULL情况
import pyodbc import pandas as pd conx_string = "driver={SQL SERVER}; server=mssql_db; database=db; UID=usr; PWD=my_pwd;" conn = pyodbc.connect(conx_string) crsr = conn.cursor() # 修复SQL语法错误 query = "select OrderID, OrderDate, OrigOrderID from tbl_Orders" data = crsr.execute(query) rows = [list(x) for x in data] columns = [column[0] for column in crsr.description] df = pd.DataFrame(rows, columns=columns) # 初始化新增列 df['Date of receipt of OrigOrder'] = None for i, row in df.iterrows(): orig_order_id = row['OrigOrderID'] if orig_order_id is not None: # 参数化查询,避免注入风险 crsr.execute("select OrderDate from tbl_Orders where OrderID=?", (orig_order_id,)) result = crsr.fetchone() if result: df.at[i, 'Date of receipt of OrigOrder'] = result[0] print(df.at[i, 'Date of receipt of OrigOrder'])
但这种逐行查询的方式性能极差,数据量大时强烈不建议使用。
内容的提问来源于stack exchange,提问作者Amy Finland
相关产品推荐
相关产品推荐

