Переменные привязки Python cx_Oracle

Я новичок в Python, у меня проблемы с использованием переменных связывания. Если я выполняю код ниже, все работает нормально.

bind= {"var" : "ciao"}
sql = "select * from sometable where somefield = :bind"
cur.prepare(sql)
cur.execute(sql,bind)

Вместо этого, если я добавлю другую переменную связывания, я получаю сообщение об ошибке.

bind= {"var" : "ciao"}
sql = "select * from sometable where somefield = :bind and otherfield = :bind"
cur.prepare(sql)
cur.execute(sql,(bind,bind))

cur.execute(sql,(bind,bind))
Oracle.NotSupportedError: Variable_TypeByValue(): unhandled data

Я решил это с

cur.execute(sql,(bind["var"],bind["var"]))

но я не могу понять, почему предыдущая команда не была в порядке.

Как правильно использовать переменные связывания? Я использую cx_Oracle.


person Giovanni De Ciantis    schedule 30.09.2015    source источник


Ответы (2)


Вы неправильно используете привязку.

Существует три разных способа связывания переменных с cx_Oracle, как можно см. здесь :

1) путем передачи кортежа в оператор SQL с пронумерованными переменными :

sql = "select * from sometable where somefield = :1 and otherfield = :2"
cur.execute(sql, (aValue, anotherValue))

2) Путем передачи аргументов ключевого слова оператору SQL с именованными переменными:

sql = "select * from sometable where somefield = :myField and otherfield = :anotherOne"
cur.execute(sql, myField=aValue, anotherOne=anotherValue)

3) Передав словарь оператору SQL с именованными переменными:

sql = "select * from sometable where somefield = :myField and otherfield = :anotherOne"
cur.execute(sql, {"myField":aValue, "anotherOne":anotherValue})

Примечания

Почему тогда ваш код работает?

Попробуем понять, что здесь происходит:

bind= {"var" : "ciao"}
sql = "select * from sometable where somefield = :bind and otherfield = :bind"
cur.execute(sql,(bind["var"], bind["var"]))

Oracle поймет, что он ожидает одну переменную. Это именованная переменная, связанная по имени bind. Затем вы должны указать параметр как именованный параметр, например:

cur.execute(sql, bind="ciao")

Или с помощью словаря, например:

cur.execute(sql, {bind:"ciao"})

Однако, поскольку вместо этого cx_Oracle получает кортеж, он отступает в привязке по номеру, как если бы ваш оператор SQL был:

sql = "select * from sometable where somefield = :1 and otherfield = :2"

И поскольку вы дважды передаете bind['var'], это просто строка "ciao". Он сопоставляет два элемента кортежа с пронумерованными переменными:

cur.execute(sql, ("ciao", "ciao"))

Это запускается случайно, но код очень вводит в заблуждение.

Кортеж с одним значением для привязки

Также обратите внимание, что для первого варианта требуется кортеж. Но если у вас есть одно значение для привязки, вы можете использовать эту нотацию для создания кортежа одного значения:

sql = "select * from sometable where somefield = :1"
cur.execute(sql, (aValue,))

[EDIT]: спасибо @tyler-christian за упоминание о том, что передача dict поддерживается cx_Oracle.

person ffarquet    schedule 23.11.2015
comment
Для меня отсутствовала конечная запятая в кортеже из одного элемента. О! - person Rob; 23.01.2018
comment
@Rob, действительно: type((1,)) = ‹type 'tuple'›, но type((1)) = ‹type 'int'› - person ffarquet; 24.01.2018
comment
Во втором и третьем примерах есть ли опечатка в операторе SQL (должно ли my_field быть myField) или это вообще не имеет значения? - person csar; 28.01.2020

@ffarquest говорит, что использование словаря не поддерживается cx_Oracle, но на самом деле @giovanni-de-ciantis просто использовал его неправильно.


named_params = {'dept_id':50, 'sal':1000}
query1 = cursor.execute(
    'SELECT * FROM employees WHERE department_id=:dept_id AND salary>:sal',
    named_params
)

OR

query2 = cursor.execute(
    'SELECT * FROM employees WHERE department_id=:dept_id AND salary>:sal',
    dept_id=50,
    sal=1000
)

Я считаю, что в данном примере вторую ссылку на :bind нужно будет заменить чем-то другим, потому что это делается не в последовательном порядке. Также переименовал переменную bind, чтобы не путаться.

bind_dict = {bind:"var" : diff:"ciao"}
sql = "select * from sometable where somefield=:bind and otherfield=:diff"
cur.prepare(sql)
cur.execute(sql, bind_dict )

В этой статье 2007 года показано, что вы можете использовать словарь: http://www.oracle.com/technetwork/articles/dsl/prez-python-queries-101587.html

person Tyler Christian    schedule 12.04.2017
comment
Спасибо за указание на то, что использование словаря действительно возможно. Мой ответ был обновлен соответственно. - person ffarquet; 06.11.2017