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

时间:2014-06-27 18:15:35

标签: python sql sql-server stored-procedures pyodbc

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

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

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

time.sleep(1)

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

是否有更优雅,更省时的方式说,"睡觉直到执行完成"?

更新(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.235\SQLEXPRESS;
    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()

5 个答案:

答案 0 :(得分:1)

没有内置的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

在示例中,func的执行在async_function_call完成后继续执行。这样,对asnyc_function_call的后续调用不会重叠,您不必通过time.sleep调用暂停执行主进程。

答案 1 :(得分:1)

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

问题不在于异步调用。

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

使用SET NOCOUNT ON运行您的过程。另外,请确保您可能用于调试的任何PRINT语句或RAISERROR被静音。

在proc中添加一个@muted之类的BIT参数,并且仅在0时才会引发调试消息。

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

总而言之,按照

的方式进行操作

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

,PYODBC将等待proc完成。

答案 2 :(得分:1)

我找到了一种不需要“静音”您的存储过程或以任何方式更改它们的解决方案。根据{{​​3}}:

<块引用>

nextset()

此方法将使光标跳到下一个可用结果 set,丢弃当前结果集中的任何剩余行。如果 没有更多的结果集,该方法返回 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()

答案 3 :(得分:0)

这是我的解决方法:

在数据库中,我创建了一个名为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。

这有点笨拙,但它对我很有用!

答案 4 :(得分:0)

我认为我的方式稍微粗糙但同时更容易理解:

function show() {
var mainDiv = document.getElementById("main");
mainDiv.style.display= "block";
mainDiv.addEventListener("click", hide);
}

function hide(e) {
   let targetElement = e.target;

   if(targetElement == document.getElementById("form")) {
      console.log("inside");
      return;
   } else {
      document.getElementById("main").style.display="none";
      console.log("outside");
    }
}

while&#34; SPNAME&#34;返回&#34;工作没有运行&#34;从jobactivity表休眠1秒,然后再次检查结果。 这个工作适用于sql job,对于SP应该喜欢在另一个表中