#python #mysql
#python #mysql #хранимые процедуры
Вопрос:
По какой-то странной причине я не могу получить результаты от вызова callproc в тестовом приложении Python. Хранимая процедура в 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
Теперь, используя PyCharm с Python 3.3, я, похоже, ничего не могу извлечь при вызове этой хранимой процедуры. Этот код дает мне желаемые результаты:
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()
Но этот код с любым курсором.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.Ошибка интерфейса: нет результирующего набора для извлечения.» Существует дополнительное странное поведение при использовании метода 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.Ошибка интерфейса: Используйте cmd_query_iter для инструкций с несколькими запросами, за которыми «следует»mysql.connector.errors.Ошибка интерфейса: используйте multi = True при выполнении нескольких инструкций » несмотря на то, что я возвращаю только один результат запроса, а не несколько наборов результатов. Обрабатывает ли соединитель MySQL Python вызов execute для хранимой процедуры как двойной запрос? Как я могу просто вызвать хранимую процедуру и получить свои результаты обратно? Я действительно не хочу динамический SQL в своем коде. Заранее спасибо за любой совет!
Ответ №1:
Вы пробовали выбирать один из наборов результатов?
for result in cursor.stored_results():
people = result.fetchall()
Возможно, он выделяет для нескольких наборов результатов, даже если у вас есть только один SELECT
stmt. Я знаю, что хранимые процедуры MySQLi в PHP делают это, чтобы разрешить возврат переменных 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()
Ответ №2:
Получение результата хранимой процедуры после вызова cursor.callproc
зависит от этих факторов:
- присваивается ли результат вызова процедуры параметру INOUT или OUT
- состоит ли результат из одной строки или результирующего набора (или результирующих наборов)
- пакет python, используемый для выполнения вызова
В спецификации DBAPI об этом говорится cursor.callproc
:
Вызовите хранимую процедуру базы данных с заданным именем. Последовательность параметров должна содержать одну запись для каждого аргумента, ожидаемого процедурой. Результат вызова возвращается как измененная копия входной последовательности. Входные параметры оставлены нетронутыми, выходные параметры заменены, возможно, новыми значениями.
Процедура также может предоставлять результирующий набор в качестве выходных данных. Затем это должно быть доступно через стандарт .методы выборки *().
На практике использование возвращаемого значения cursor.callproc
может работать только в том случае, если процедура возвращает одну строку с количеством столбцов, соответствующим количеству параметров INOUT и OUT, поэтому существуют некоторые различия в способе обработки результатов.
Вот как эти случаи обрабатываются основными пакетами MySQL Python connector — MySQL Connector, mysqlclient (MySQLdb) и PyMySQL.
Результат в одной строке, возвращаемый с помощью параметров INOUT или OUT
-
Соединитель MySQL возвращает измененную копию входной последовательности в качестве возвращаемого значения
cursor.callproc
; значение является кортежем.params = [in_param, out_param1, out_param2] in_, out1, out2 = cursor.callproc("test_proc", params)
-
mysqlclient и PyMySQL требуют запроса выходных параметров к базе данных, а затем извлечения результатов с помощью курсора; значение представляет собой набор кортежей. Имена параметров, которые будут запрошены, имеют вид
'@_{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()]
-
mysqlclient и PyMySQL предоставляют результат с помощью методов выборки курсора *
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()]
-
mysqlclient и PyMySQL требуют, чтобы каждый результирующий набор извлекался с помощью курсора при вызове 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
Комментарии:
1. Очень приятно.
results = [r.fetchall() for r in cursor.stored_results()]
это именно то, что мне было нужно.
Ответ №3:
Почему бы не попробовать это таким образом
cursor.callproc("getperson", ['1'])
Комментарии:
1. Разве это не было бы больше похоже на синтаксис SQL Server, чем MySQL?
2. Потому что это недопустимая команда в PL / SQL MySQL. Это допустимая команда для T-SQL SQL SQL сервера, но это не то, что я использую для этого проекта. Синтаксис MySQL для вызова хранимых процедур является
call proc_name([arguments])
.