让python等待存储过程完成执行



我有一个python脚本,它使用pyodbc调用MSSQL存储过程,如下所示:

cursor.execute("exec MyProcedure @param1 = '" + myparam + "'")

我在循环中调用这个存储过程,我注意到,有时,在最后一次执行完成之前,这个过程会被再次调用。我知道这个是因为如果我加上

time.sleep(1)

在执行行之后,一切正常。

有没有一种更优雅、更省时的方式来表达"sleep until the exec Is finished"?

更新(Divij的解决方案):这段代码目前不能为我工作:

from tornado import gen
import pyodbc
@gen.engine
def func(*args, **kwargs):
    # connect to db
    cnxn_str = """
    Driver={SQL Server Native Client 11.0};
    Server=172.16.111.235SQLEXPRESS;
    Database=CellTestData2;
    UID=sa;
    PWD=Welcome!;
    """
    cnxn = pyodbc.connect(cnxn_str)
    cnxn.autocommit = True
    cursor = cnxn.cursor()
    for _ in range(5):
        yield gen.Task(cursor.execute, 'exec longtest')
    return
func()

我知道这是旧的,但我刚刚花了几个小时试图弄清楚如何让我的Python代码等待MSSQL上的存储过程完成。

问题不在于异步调用。

解决此问题的关键是确保您的过程在完成运行之前不返回任何消息。否则,PYDOBC会将来自进程的第一条消息解释为进程的结束。

SET NOCOUNT ON运行你的程序。此外,确保您可能用于调试的任何PRINT语句或RAISERROR都是静音的。

添加一个BIT参数,如@muted到您的进程,只有当它是0时,才会引发您的调试消息。

在我的特殊情况下,我正在执行一个进程来处理一个加载的表,我的应用程序在过程完成运行之前退出并关闭游标,因为我正在获取行计数和调试消息。

所以总结一下,按照

的思路做一些事情

cursor.execute('SET NOCOUNT ON; EXEC schema.proc @muted = 1')

和PYODBC将等待进程完成。

这是我的解决方案:

在数据库中,我创建了一个名为RunningStatus的表,其中只有一个字段status,这是一个bit,只有一行,初始设置为0。

在存储过程的开头,我执行

update RunningStatus set status = 1;

在存储过程的末尾,

update RunningStatus set status = 0;

在我的Python脚本中,我打开一个新连接并游标到同一个数据库。在execute行之后,我只需添加

while 1:
    q = status_check_cursor.execute('select status from RunningStatus').fetchone()
    if q[0] == 0:
        break

您需要创建一个新的连接和游标,因为来自旧连接的任何调用都将中断存储过程,并且可能导致status永远不会返回0。

这是一个小麻烦,但它对我来说工作得很好!

我已经找到了一个解决方案,不需要"muting"您的存储过程或以任何方式更改它们。根据pyodbc wiki:

nextset ()

此方法将使游标跳转到下一个可用结果设置,丢弃当前结果集中的任何剩余行。如果如果没有更多的结果集,则该方法返回False。否则,它返回True,后续对fetch方法的调用将返回下一个结果集中的行。

此方法主要用于存储过程返回多个结果。

若要等待存储过程完成执行,然后再继续执行程序的其余部分,请在执行游标中运行存储过程的代码后使用以下代码:

slept = 0
while cursor.nextset():
    if slept >= TIMEOUT:
        break
    time.sleep(1)
    slept += 1

您也可以将time.sleep()的值从1秒更改为略低于一秒,以减少额外的等待时间,但我不建议在一秒钟内调用它很多次。

下面是一个完整的程序,展示了如何实现这段代码:

import time
import pyodbc
connection = pyodbc.connect('DRIVER={SQL Server};SERVER=<hostname>;PORT=1433;DATABASE=<database name>;UID=<database user>;PWD=password;CHARSET=UTF-8;')
cursor = connection.cursor()
TIMEOUT = 20  # Max number of seconds to wait for procedure to finish execution
params = ['value1', 2, 'value3']
cursor.execute("BEGIN EXEC dbo.sp_StoredProcedureName ?, ?, ? END", *params)
# here's where the magic happens with the nextset() function
slept = 0
while cursor.nextset():
    if slept >= TIMEOUT:
        break
    time.sleep(1)
    slept += 1
cursor.close()
connection.close()

python没有内置允许您等待异步调用完成的功能。然而,你可以使用Tornado的IOLoop实现这种行为。Tornado的gen接口允许您将函数调用注册为Task,并在调用完成执行后返回到函数的下一行。下面是使用gengen.Task的示例

from tornado import gen
@gen.engine
def func(*args, **kwargs)
    for _ in range(5):
        yield gen.Task(async_function_call, arg1, arg2)
    return

在本例中,async_function_call执行完毕后,func继续执行。这样对asnyc_function_call的后续调用就不会重叠,并且您不必在调用time.sleep时暂停主进程的执行。

我认为我的方法有点粗糙,但同时也更容易理解:

cursor = connection.cursor()
    SQLCommand = ("IF EXISTS(SELECT 1 FROM msdb.dbo.sysjobs J JOIN 
msdb.dbo.sysjobactivity A ON A.job_id = J.job_id WHERE J.name ='dbo.SPNAME' AND 
A.run_requested_date IS NOT NULL AND A.stop_execution_date IS NULL) select 'The job is 
running!' ELSE select 'The job is not running.'")
    cursor.execute(SQLCommand)
    results = cursor.fetchone()
    sresult= str(results)
    while "The job is not running" in sresult:
        time.sleep(1)
        cursor.execute(SQLCommand)
        results = cursor.fetchone()
        sresult= str(results)

当"SPNAME"从jobactivity表返回"作业未运行"时,休眠1秒并再次检查结果。此工作为sql作业,对于SP应该像在另一个表

相关内容

  • 没有找到相关文章

最新更新