由于需要使用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)
不过网上很多文章都是误导,例如:Python连接数据库-pyodbc
里面说运行存储过程使用 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就好了。
如果需要得到返回数据(即存储过程里面有 Select出来的内容),就需要加上SET NOCOUNT ON;参数。参考资料:MSSQL2008 - Pyodbc - Previous SQL was not a query
所以我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','' --不带参