遭瘟的pyodbc——關於儲存過程執行
由於需要使用django連線SQL Server,煩人的事情隨之而來。
首先Django 沒有自帶針對MSSQL的BackEnds,所以要自己包裝,這個很煩很煩,煩到死了,每次部署到新的機器上都要除錯很久。
可以見我之前寫的這篇文章:Django連線SQL Server配置指引,其實是可以成功的,但是每次都要配置,太過麻煩了。
而且只能連線一個數據庫,如果要連線多個數據庫,無法避免要使用pyodbc(或者其它第三方包)來連線。
pyodbc,執行Query SQL是不在話下的,非常簡單,上面說的文章也有說使用方法:
import pyodbc connection = pyodbc.connect('DRIVER={SQL Server Native Client 10.0};SERVER=127.0.0.1;DATABASE=DB_name;UID=User_Name;PWD=PassWord') curs = connection.execute('select * from some_table') curs.fetchone()
但是如果要執行儲存過程,就痛苦了。
---------------------------------------
冷靜一下。
先說說pyodbc的基本用法:
1. 先匯入:from pyodbc import connect;
2. 產生連線例項:Conn = connect(DBCONNECTSTR);
3. 產生遊標:cur = Conn.cursor();
4. 執行遊標命令:cur.execute()、cur.commit()、cur.rollback()之類;
5. 關閉遊標:cur.close();
6. 關閉連線例項:Conn.close()。
以上6個步驟都是必要的。
當然,如果僅僅是查詢,可以直接使用例項直接執行excute命令就可以了,可以不建立遊標,那麼3、4、5步都可以省略。
上面這些內容網上一大堆教程,我就不多說了。(DRY=Donot Repeat Yourself)
裡面說執行儲存過程使用 callproc() 方法。
我勒個去,這個方法完全是她YY出來的。
提供 connection 和 cursor 的方法列表:
>>> dir(Conn)
['__class__', '__delattr__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'add_output_converter', 'autocommit', 'clear_output_converters', 'close'
>>> dir(cur)
['__class__', '__delattr__', '__doc__', '__enter__', '__exit__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__new__', '__reduce__','__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'arraysize', 'close', 'columns', 'commit', 'connection', 'description', 'execute', 'executemany', 'fetchall', 'fetchmany', 'fetchone', 'foreignKeys', 'getTypeInfo', 'next', 'nextset', 'noscan', 'primaryKeys', 'procedureColumns', 'procedures', 'rollback', 'rowIdColumns', 'rowVerColumns', 'rowcount', 'setinputsizes', 'setoutputsize', 'skip', 'statistics', 'tables']
常用的方法和標量我都標記為紅色了。
迴歸正題,執行儲存過程用哪個方法呢?不用多想了,還是execute(),然後附加 EXEC 當作SQL命令來執行。
try:
#不關心返回資料儲存過程執行方法
cur.execute('EXEC P_THIS_IS_A_PROCDURE param1,param2,%d,%s' %(p3_int,p4_str))
print cur.rowcount #可以得到儲存過程影響的行數
#如果你關心返回值,需要使用這種方式
rows = cur.execute('SET NOCOUNT ON; EXEC P_THIS_IS_A_PROCDURE param1,param2,%d,%s' %(p3_int,p4_str)).fetchall()
print cur.rowcount,rows #聰明的你已經知道,行數肯定是木有的了(-1),後面的rows是一個列表,看你的資料是咋樣的了。
#無論何種方式,都可以最後才commit(其實方式2已經預設包含了commit)
cur.commit()
except Exception as e:
print e
上面已經寫得比較清楚了,直接執行儲存過程就使用EXEC就好了。
所以我TMD為了配合pyodbc,儲存過程都需要加上一個selet語句,把return值給select出來。
我已經吐了。
變通的方法:
可以寫一個外層儲存過程,統一呼叫這個儲存過程,把返回值給select出來就好了。
CREATE PROCEDURE P_RUN_PROCDURE_WITH_SELECTRETURN
@I_ProcName NVARCHAR(1000) ,
@I_Params NVARCHAR(1000)
AS
DECLARE @SqlStr NVARCHAR(MAX)
/**
* 返回值列表:
* 0 正常退出,儲存過程執行成功
* 1 異常退出,引數校驗失敗
* ……這個隨便你寫了,主要是和你儲存過程裡面的return相關
*/
SET @SqlStr = 'DECLARE @res INT; EXEC @res = ' + @I_ProcName + ' ' + @I_Params + '; SELECT @res'
EXEC(@SqlStr)
GO
呼叫方法:
EXEC P_RUN_PROCDURE_WITH_SELECTRETURN 'P_DIM_CONSTANT_TOGGLESTATUS','100,''some_string'',123.45' --帶參
EXEC P_RUN_PROCDURE_WITH_SELECTRETURN 'P_DIM_AMB_PATHCREATE','' --不帶參