Недопустимое имя / номер переменной при передаче в списке Python

#python #sql #oracle #cx-oracle

#python #sql #Oracle #cx-oracle

Вопрос:

Я пытаюсь запустить операторы SQL через Python в списке. Путем передачи списка, в данном случае даты. Поскольку я хочу запустить несколько запросов SELECT SQL и вернуть их. Я проверил это, передав целые числа, однако при попытке передать дату я получаю ошибку ORA-01036. Недопустимое имя / номер переменной. Я использую Oracle DB.

 cursor = connection.cursor()
date = ["'01-DEC-21'", "'02-DEC-21'"]

sql = "select * from table1 where datestamp = :date"

for item in date:
    cursor.execute(sql,id=item)
    res=cursor.fetchall()
    print(res)
 

Есть предложения по выполнению этого запуска?

Комментарии:

1. используйте IN вместо =, = для одного значения, а IN — для списка

2. @AhmedSunny спасибо за эту информацию. Это большая таблица, и иногда для ее анализа требуется больше времени, чем для выполнения по одному за раз

3. Прочитайте документацию cx_Oracle, связывающую несколько значений с предложением SQL WHERE IN , в котором есть некоторые общие решения.

Ответ №1:

Вы не можете назвать переменную привязки date , это недопустимое имя. Также ваша именованная переменная в cursor.execute должна соответствовать имени переменной привязки. Попробуйте что-то вроде:

 sql = "select * from table1 where datestamp = :date_input"

for item in date:
  cursor.execute(sql,date_input=item)
  res=cursor.fetchall()
  print(res)
 

Ответ №2:

Некоторые рекомендации и предупреждения для вашего подхода:

  • вы не должны зависеть от настройки NLS даты по умолчанию при привязке a String (например "'01-DEC-21'" ) к DATE столбцу. (Вероятно, вам также нужно изменить одну из кавычек).
  • Вы должны отказаться от извлечения данных в цикле, если вы можете получить их в одном запросе (используя IN список)
  • используйте подготовленный оператор

Пример

 date = ['01-DEC-21', '02-DEC-21']
 

Это генерирует запрос, который использует переменные привязки для вашего входного списка

 in_list = ','.join([f" TO_DATE(:d{ind},'DD-MON-RR','NLS_DATE_LANGUAGE = American')" for ind, d in enumerate(date)])

sql_query = "select * from table1 where datestamp in ( "   in_list   " )"
 

sql_query Генерируется

 select * from table1 where datestamp in 
(  TO_DATE(:d0,'DD-MON-RR','NLS_DATE_LANGUAGE = American'), TO_DATE(:d1,'DD-MON-RR','NLS_DATE_LANGUAGE = American') )
 

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

Обратите также внимание на использование to_date с явной маской и исправление языка, чтобы избежать проблем с интерпретацией сокращения месяца. (например ORA-01843: not a valid month )

Теперь вы можете использовать запрос для извлечения данных за один проход

 cur.prepare(sql_query)
cur.execute(None, date)
res = cur.fetchall()
 

Комментарии:

1. Спасибо за этот ввод. Я изменил, и теперь я получаю cx_Oracle.DatabaseError: ORA-01830: date format picture ends before converting entire input string

2. Поскольку Oracle имеет кэш инструкций , может быть мало пользы от явного prepare()

3. Это верно, а также кэш курсора работает нормально @ChristopherJones. Итак, в этом простом примере видимого эффекта не будет. В любом случае, рекомендация IMO использовать подготовленные операторы в крупномасштабных приложениях остается в силе.