将存储过程选择结果读入pandas数据帧

时间:2014-10-01 01:22:15

标签: sql-server stored-procedures pandas sqlalchemy pyodbc

假设:

CREATE PROCEDURE my_procedure
    @Param INT
AS
    SELECT Col1, Col2
    FROM Table
    WHERE Col2 = @Param

我希望能够将其用作:

import pandas as pd
import pyodbc

query = 'EXEC my_procedure @Param = {0}'.format(my_param)
conn = pyodbc.connect(my_connection_string)

df = pd.read_sql(query, conn)

但这会引发错误:

ValueError: Reading a table with read_sql is not supported for a DBAPI2 connection. Use an SQLAlchemy engine or specify an sql query

SQLAlchemy也不起作用:

import sqlalchemy
engine = sqlalchemy.create_engine(my_connection_string)
df = pd.read_sql(query, engine)

抛出:

ValueError: Could not init table 'my_procedure'

我实际上可以直接使用pyodbc执行语句:

cursor = conn.cursor()
cursor.execute(query)
results = cursor.fetchall()
df = pd.DataFrame.from_records(results)

有没有办法将这些程序结果直接发送到DataFrame?

4 个答案:

答案 0 :(得分:4)

https://code.google.com/p/pyodbc/wiki/StoredProcedures

我不是python专家,但SQL Server有时会返回语句执行的计数。例如,更新将告知更新了多少行。

只需使用' SET NO COUNT;'在批量电话的前面。这将删除插入,更新和删除的计数。

确保使用正确的本机客户端模块。

看看这个堆栈溢出示例。

它有一个特殊的SQL和调用存储过程示例。

Calling a stored procedure python

祝你好运

答案 1 :(得分:4)

改为使用 read_sql_query()

看起来@joris(+1)已直接在问题的评论中有这个,但我没有看到它,因为它不在答案部分。

使用SQLA引擎 - 除了SQLAlchemy之外,Pandas只支持SQLite。然后使用 read_sql_query() 而不是read_sql()。后者尝试自动检测您是在传递表名还是完全成熟的查询,但是使用'EXEC'关键字看起来效果不佳。使用read_sql_query()会跳过自动检测,并允许您明确指出您正在使用查询(还有一个read_sql_table())。

import pandas as pd
import sqlalchemy

query = 'EXEC my_procedure @Param = {0}'.format(my_param)
engine = sqlalchemy.create_engine(my_connection_string)
df = pd.read_sql_query(query, engine)

答案 2 :(得分:1)

使用ODBC语法调用存储过程(使用参数而不是字符串格式)适用于使用pandas 0.14.1和pyodbc 3.0.7加载数据帧。以下示例使用AdventureWorks2008R2 sample database

首先使用pyodbc确认调用存储过程的预期结果:

import pandas as pd
import pyodbc
connection = pyodbc.connect(driver='{SQL Server Native Client 11.0}', server='ServerInstance', database='AdventureWorks2008R2', trusted_connection='yes')
sql = "{call dbo.uspGetEmployeeManagers(?)}"
params = (3,)
cursor = connection.cursor()
rows = cursor.execute(sql, params).fetchall()
print(rows)

应该返回:

[(0, 3, 'Roberto', 'Tamburello', '/1/1/', 'Terri', 'Duffy'), (1, 2, 'Terri', 'Duffy',
'/1/', 'Ken', 'Sánchez')]

现在使用pandas将结果加载到数据帧中:

df = pd.read_sql(sql=sql, con=connection, params=params)
print(df)

应该返回:

   RecursionLevel  BusinessEntityID FirstName    LastName OrganizationNode  \
0               0                 3   Roberto  Tamburello            /1/1/
1               1                 2     Terri       Duffy              /1/

  ManagerFirstName ManagerLastName
0            Terri           Duffy
1              Ken         Sánchez

修改

由于您无法更新到pandas 0.14.1,请使用pandas.DataFrame.from_records从pyodbc加载结果:

# get column names from pyodbc results
columns = [column[0] for column in cursor.description]
df = pd.DataFrame.from_records(rows, columns=columns)

答案 3 :(得分:1)

在添加SET NOCOUNT ON谢谢@CRAFTY DBA后,这对我有用

sql_query = """SET NOCOUNT ON; EXEC db_name.dbo.StoreProc '{0}';""".format(input)

df = pandas.read_sql_query(sql_query , conn)