如何合并SQL Server多结果集并导入至Python DataFrame
解决方法
方法1:修改SQL脚本生成单一结果集(推荐)
由于不同表的结构可能存在差异,直接返回多个结果集难以合并,我们可以修改SQL逻辑,把每个表的首行数据转成数据库名-表名-列名-列值的统一格式,用UNION ALL拼接成单一结果集,这样pandas可以直接读取全部数据。
SQL脚本
-- 定义要排除的数据库,用逗号分隔 DECLARE @ExcludedDBs NVARCHAR(MAX) = 'master,model,msdb,tempdb,BizDB1,BizDB2'; EXEC sp_MSforeachdb ' -- 跳过排除的数据库 IF NOT EXISTS (SELECT 1 FROM STRING_SPLIT(''@ExcludedDBs'', '','') WHERE value = ''?'') BEGIN -- 遍历当前数据库下的所有表 EXEC [?].dbo.sp_MSforeachtable '' SELECT DB_NAME() AS DatabaseName, ''?'' AS TableName, c.name AS ColumnName, v.ColumnValue FROM ( -- 获取当前表的首行数据 SELECT TOP 1 * FROM ? ) t -- 将行转列,统一格式 UNPIVOT ( ColumnValue FOR ColumnName IN (' + STUFF((SELECT ',' + QUOTENAME(name) FROM [?].sys.columns WHERE object_id = OBJECT_ID('?') FOR XML PATH('')), 1, 1, '') + ') ) v JOIN [?].sys.columns c ON v.ColumnName = QUOTENAME(c.name) '' END '
Python读取代码
import pyodbc import pandas as pd # 替换为你的数据库连接信息 conn_str = ( r'DRIVER={ODBC Driver 17 for SQL Server};' r'SERVER=你的服务器名;' r'DATABASE=master;' r'UID=你的用户名;' r'PWD=你的密码;' ) with pyodbc.connect(conn_str) as conn: sql = """ DECLARE @ExcludedDBs NVARCHAR(MAX) = 'master,model,msdb,tempdb,BizDB1,BizDB2'; EXEC sp_MSforeachdb ' IF NOT EXISTS (SELECT 1 FROM STRING_SPLIT(''@ExcludedDBs'', '','') WHERE value = ''?'') BEGIN EXEC [?].dbo.sp_MSforeachtable '' SELECT DB_NAME() AS DatabaseName, ''?'' AS TableName, c.name AS ColumnName, v.ColumnValue FROM ( SELECT TOP 1 * FROM ? ) t UNPIVOT ( ColumnValue FOR ColumnName IN (' + STUFF((SELECT ',' + QUOTENAME(name) FROM [?].sys.columns WHERE object_id = OBJECT_ID('?') FOR XML PATH('')), 1, 1, '') + ') ) v JOIN [?].sys.columns c ON v.ColumnName = QUOTENAME(c.name) '' END ' """ # 直接读取单一结果集 combined_df = pd.read_sql(sql, conn) print(combined_df.head())
方法2:Python端遍历所有结果集合并
如果不想修改SQL逻辑,可以通过pyodbc的cursor.nextset()方法遍历所有返回的结果集,逐个转成DataFrame后合并。注意要在SQL中给每个结果集加上数据库名和表名,否则无法区分数据来源。
SQL脚本
DECLARE @ExcludedDBs NVARCHAR(MAX) = 'master,model,msdb,tempdb,BizDB1,BizDB2'; EXEC sp_MSforeachdb ' IF NOT EXISTS (SELECT 1 FROM STRING_SPLIT(''@ExcludedDBs'', '','') WHERE value = ''?'') BEGIN EXEC [?].dbo.sp_MSforeachtable '' SELECT DB_NAME() AS DatabaseName, ''?'' AS TableName, * FROM ? TOP 1 '' END '
Python合并代码
import pyodbc import pandas as pd conn_str = ( r'DRIVER={ODBC Driver 17 for SQL Server};' r'SERVER=你的服务器名;' r'DATABASE=master;' r'UID=你的用户名;' r'PWD=你的密码;' ) conn = pyodbc.connect(conn_str) cursor = conn.cursor() sql = """ DECLARE @ExcludedDBs NVARCHAR(MAX) = 'master,model,msdb,tempdb,BizDB1,BizDB2'; EXEC sp_MSforeachdb ' IF NOT EXISTS (SELECT 1 FROM STRING_SPLIT(''@ExcludedDBs'', '','') WHERE value = ''?'') BEGIN EXEC [?].dbo.sp_MSforeachtable '' SELECT DB_NAME() AS DatabaseName, ''?'' AS TableName, * FROM ? TOP 1 '' END ' """ cursor.execute(sql) dfs = [] while True: try: # 获取当前结果集的列名 cols = [col[0] for col in cursor.description] # 获取所有行数据 rows = cursor.fetchall() # 转成DataFrame并加入列表 dfs.append(pd.DataFrame.from_records(rows, columns=cols)) # 切换到下一个结果集 cursor.nextset() except pyodbc.ProgrammingError: # 没有更多结果集时退出循环 break # 合并所有DataFrame,缺失列自动填充NaN combined_df = pd.concat(dfs, ignore_index=True) # 清理资源 cursor.close() conn.close() print(combined_df.head())
注意事项
- 方法1更高效,适合表数量较多的场景,减少了Python与数据库的交互次数。
- 方法2会保留每个表的原始结构,合并后DataFrame会包含所有表的列,不存在的列值为
NaN。 - 确保SQL Server版本支持
STRING_SPLIT(2016及以上),旧版本需替换为自定义字符串分割函数。 - 执行存储过程需要足够的数据库权限(如
db_owner或等效权限)。
内容的提问来源于stack exchange,提问作者user_v27
相关产品推荐
相关产品推荐

