2014-10-01 47 views
4

考虑:读取存储过程的选择结果为大熊猫数据帧

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) 

有没有办法来直接将这些程序结果的数据帧?

+2

您使用的是什么版本的熊猫?你可以尝试使用'pd.read_sql_query'而不是'read_sql'吗? ('read_sql'中存在一个关于执行存储过程的错误) – joris 2014-10-01 08:06:06

回答

3

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

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

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

确保您使用的是正确的本地客户端模块。

看看这个堆栈溢出的例子。

它有一个adhoc SQL和调用存储过程的例子。

Calling a stored procedure python

好运

+0

'SET NOCOUNT ON'不幸在这种情况下不起作用。 – joeb1415 2014-10-01 16:58:08

+0

在我的存储过程开始时添加SET NOCOUNT ON。谢谢。另外,我尝试在查询的开始处添加“SET NOCOUNT ON;”,这也起作用。 – FistOfFury 2017-03-13 14:48:32

1

使用ODBC语法调用存储过程(与参数,而不是字符串格式化)适用于使用熊猫0.14.1和pyodbc 3.0.7装载dataframes。以下示例使用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')] 

现在使用熊猫的结果加载到数据帧:

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 

编辑

既然你不能更新到0.14大熊猫。1,从pyodbc使用pandas.DataFrame.from_records加载的结果:

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

这在熊猫0.14.0中不起作用。我目前无法升级到0.14.1以在那里测试,但感谢提示。 – joeb1415 2014-10-01 16:54:57

+0

@ joeb1415更新了如何在不使用'read_sql'的情况下填充数据框 – Bryan 2014-10-01 17:13:55

1

使用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)