1. 程式人生 > >遭瘟的pyodbc——關於儲存過程執行

遭瘟的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'

, 'commit', 'cursor', 'execute', 'getinfo', 'rollback', 'searchescape', 'timeout']

>>> 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',''   --不帶參