将含临时表的复杂SQL导入Pandas仅得1列,遇形状错误求助
解决带临时表的SQL查询导入Pandas DataFrame的问题
我来帮你搞定这个问题!你的核心问题出在多次调用cursor.execute()会覆盖游标状态,而且没有正确处理多语句执行后的结果集,导致最后只拿到了某一个单列查询的结果。下面给你详细的解决方案和原因分析:
问题根源
每次调用cursor.execute()时,游标会重置到新的查询结果上。如果你的最后一次execute()执行的不是那个返回聚合结果的查询,或者前面的临时表创建语句没有和最终查询在同一个逻辑执行块里,就会出现要么结果不对、要么只有1列的情况。另外,创建临时表的DDL语句通常不会返回数据结果集,直接fetchall()只会拿到最后一次执行的语句的结果。
解决方案一:合并SQL语句并手动处理结果集
把所有创建临时表和最终聚合查询的SQL合并成一个脚本,用分号分隔,然后执行后跳过前面的空结果集,只获取最终的SELECT结果:
import pyodbc import pandas as pd cnxn = pyodbc.connect(r'DRIVER=foo;SERVER=bar;etc') cursor = cnxn.cursor() # 把所有SQL逻辑写在一个字符串里,最后一条是返回聚合结果的SELECT full_sql = """ -- 创建第一个临时表 CREATE TABLE #TempAgg1 AS SELECT category, COUNT(*) AS item_count FROM raw_data GROUP BY category; -- 创建第二个临时表(如果需要) CREATE TABLE #TempAgg2 AS SELECT category, SUM(item_count) AS total FROM #TempAgg1 GROUP BY category; -- 最终的聚合查询,这是你要导入DataFrame的结果 SELECT category, total, ROUND(total/100, 2) AS percentage FROM #TempAgg2; """ cursor.execute(full_sql) # 跳过前面创建临时表的空结果集(DDL语句不返回数据) while cursor.nextset(): pass # 现在获取最终的查询结果 rows = cursor.fetchall() columns = [desc[0] for desc in cursor.description] df = pd.DataFrame(rows, columns=columns)
解决方案二:用Pandas的read_sql_query自动处理(更简单)
Pandas的read_sql_query方法可以自动识别多语句SQL脚本中的最后一个数据结果集,省去手动处理游标的麻烦:
import pyodbc import pandas as pd cnxn = pyodbc.connect(r'DRIVER=foo;SERVER=bar;etc') full_sql = """ CREATE TABLE #TempAgg1 AS SELECT category, COUNT(*) AS item_count FROM raw_data GROUP BY category; CREATE TABLE #TempAgg2 AS SELECT category, SUM(item_count) AS total FROM #TempAgg1 GROUP BY category; SELECT category, total, ROUND(total/100, 2) AS percentage FROM #TempAgg2; """ # 直接执行整个脚本,Pandas会自动返回最终的DataFrame df = pd.read_sql_query(full_sql, cnxn)
关键注意事项
- 确保你的SQL脚本最后一条语句是返回目标聚合结果的SELECT,Pandas或游标都会优先处理最后一个返回数据的结果集。
- 临时表(比如SQL Server的
#开头的表)是会话级别的,只要连接不关闭,临时表就会存在,不用担心跨语句的可见性问题。 - 如果你的数据库不支持多语句执行(极少情况),可以把临时表逻辑改成CTE(公共表表达式),避免创建临时表:
WITH TempAgg1 AS ( SELECT category, COUNT(*) AS item_count FROM raw_data GROUP BY category ), TempAgg2 AS ( SELECT category, SUM(item_count) AS total FROM TempAgg1 GROUP BY category ) SELECT category, total, ROUND(total/100, 2) AS percentage FROM TempAgg2;
内容的提问来源于stack exchange,提问作者enumaris
相关产品推荐
相关产品推荐

