【问题标题】:Cannot return results from stored procedure using Python cursor无法使用 Python 游标从存储过程返回结果
【发布时间】:2013-02-25 13:09:19
【问题描述】:

由于某种奇怪的原因,我无法从 Python 测试应用程序中的 callproc 调用中获得结果。 MqSQL 5.2.47 中的存储过程如下所示:

CREATE PROCEDURE `mytestdb`.`getperson` (IN personid INT)
BEGIN
   select person.person_id,
          person.person_fname,
          person.person_mi,
          person.person_lname,
          person.persongender_id,
          person.personjob_id
     from person
    where person.person_id = personid;
END

现在,在 Python 3.3 中使用 PyCharm,调用此存储过程时我似乎无法检索任何内容。这段代码让我得到了想要的结果:

import mysql.connector

cnx = mysql.connector.connect(user='root', host='127.0.0.1', database='mytestdb')
cnx._open_connection()
cursor = cnx.cursor()

cursor.execute("select * from person where person.person_id = 1")
people = cursor.fetchall()

for person in people:
    print(person)

cnx.close()

但是这段代码使用 cursor.fetchall() 或 cursor.fetchone()...

import mysql.connector

cnx = mysql.connector.connect(user='root', host='127.0.0.1', database='mytestdb')
cnx._open_connection()
cursor = cnx.cursor()

cursor.callproc("getperson", [1])
people = cursor.fetchall()

for person in people:
    print(person)

cnx.close()

...返回“mysql.connector.errors.InterfaceError:没有要从中获取的结果集。”像这样使用 cursor.execute() 方法还有一个额外的奇怪行为......

import mysql.connector

cnx = mysql.connector.connect(user='root', host='127.0.0.1', database='mytestdb')
cnx._open_connection()
cursor = cnx.cursor()

cursor.execute("call getperson(1)")
people = cursor.fetchall()

for person in people:
    print(person)

cnx.close()

...因为它产生“mysql.connector.errors.InterfaceError: Use cmd_query_iter for statements with multiple queries”后跟“mysql.connector.errors.InterfaceError: Use multi=True when execution multiple statements”尽管事实上我只返回一个查询结果而不是多个结果集。 MySQL Python 连接器是否将存储过程上的执行调用视为双重查询?我怎样才能调用存储过程并取回我的结果?我真的不想在我的代码中使用动态 SQL。提前感谢您的任何建议!

【问题讨论】:

    标签: python mysql stored-procedures


    【解决方案1】:

    调用cursor.callproc后获取存储过程的结果取决于以下因素:

    • 调用过程的结果是赋值给INOUT还是OUT参数
    • 结果是由单行还是一个结果集(或多个结果集)组成
    • 用于进行调用的 python 包

    DBAPI speccursor.callproc 上有这样的说法:

    调用具有给定名称的存储数据库过程。对于过程期望的每个参数,参数序列必须包含一个条目。调用的结果作为输入序列的修改副本返回。输入参数保持不变,输出和输入/输出参数替换为可能的新值。

    该过程还可以提供一个结果集作为输出。然后必须通过标准的 .fetch*() 方法使其可用。

    在实践中,使用cursor.callproc 的返回值只能在过程返回单行且列数与 INOUT 和 OUT 参数数匹配的情况下起作用,因此在处理结果的方式上存在一些差异.


    以下是主要 MySQL Python 连接器包处理这些情况的方式 - MySQL Connectormysqlclient (MySQLdb)PyMySQL

    单行结果,通过INOUT或OUT参数返回

    • MySQL 连接器返回输入序列的修改副本作为返回值cursor.callproc;该值是一个元组。

      params = [in_param, out_param1, out_param2]
      in_, out1, out2 = cursor.callproc("test_proc", params) 
      
    • mysqlclientPyMySQL 要求查询数据库的输出参数,然后通过游标获取结果;该值是元组的元组。要查询的参数名称格式为'@_{procedure_name}_{params.index(param)}'

      cursor.callproc("test_proc", params)
      cursor.execute("""SELECT @_test_proc_0, @_test_proc_1""")
      result = cursor.fetchall()
      

    单个结果集中的一行或多行,未定义 INOUT 或 OUT 参数

    • MySQL 连接器通过游标的 stored_results 方法公开结果(cursor.stored_results 不是 DBAPI 规范的一部分)

      cursor.callproc("test_proc", params)
      results = [r.fetchall() for r in cursor.stored_results()]
      
    • mysqlclientPyMySQL 通过游标的 fetch* 方法公开结果

      cursor.callproc("test_proc", params)
      results = cursor.fetchall()
      

    多个结果集,未定义 INOUT 或 OUT 参数

    • MySQL 连接器通过游标的stored_results 方法公开结果

      cursor.callproc("test_proc", params)
      results = [r.fetchall() for r in cursor.stored_results()]
      
    • mysqlclientPyMySQL 要求通过游标获取每个结果集,同时调用cursor.nextset 前进到下一个结果集。请注意,调用过程的结果可能会返回一个额外的空结果集(如果结果集是通过cursor.nextset 检索到的,而不是只调用一次cursor.fetchall,则在前面的示例中也会发生这种情况)。

      cursor.callproc("test_proc", params)
      results = [cursor.fetchall()]
      while cursor.nextset():
          results.append(cursor.fetchall())
      

    版本信息

    $ mysql --version
    mysql  Ver 15.1 Distrib 10.1.41-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2
    
    $ pip list | grep -i mysql
    mysql-connector-python 8.0.18 
    mysqlclient            1.4.6  
    PyMySQL                0.9.3  
    

    【讨论】:

    • 非常好。 results = [r.fetchall() for r in cursor.stored_results()] 正是我所需要的。
    【解决方案2】:

    您是否尝试过选择其中一个结果集?

    for result in cursor.stored_results():
        people = result.fetchall()
    

    即使您只有一个SELECT stmt,它也可能分配给多个结果集。我知道在 PHP 的 MySQLi 存储过程中这样做是为了允许 INOUT 和 OUT 变量返回(同样,你没有,但也许它无论如何都在分配)。

    我正在使用的完整代码(正在运行)是:

    import mysql.connector
    
    cnx = mysql.connector.connect(user='me',password='pw',host='localhost',database='mydb')
    cnx._open_connection()
    cursor = cnx.cursor()
    
    cursor.callproc("getperson",[1])
    
    for result in cursor.stored_results():
        people=result.fetchall()
    
    for person in people:
        print person
    
    cnx.close()
    

    【讨论】:

      【解决方案3】:

      为什么不试试这种方式

      cursor.callproc("getperson", ['1'])
      

      【讨论】:

      • 这不是比 MySQL 更多的 SQL 服务器语法吗?
      • 因为这不是 MySQL 的 PL/SQL 中的有效命令。这是 SQL Server 的 T-SQL 的有效命令,但这不是我在这个项目中使用的。 MySQL 调用存储过程的语法是call proc_name([arguments])
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-05
      • 2013-06-21
      • 2011-10-16
      • 2017-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多