2025, Nov 06 18:02

Исправляем ORA-01036 в executemany() oracledb: порядок привязок и столбцов

Как устранить ORA-01036 в executemany() oracledb: правильные плейсхолдеры, порядок привязок и столбцов, пример кода на Python для пакетных обновлений.

Исправляем ORA-01036 в executemany(): как связываемые переменные и порядок столбцов могут сломать пакетные обновления

Введение

Пакетные обновления через executemany() в Python обычно несложны, но малейшая ошибка со связываемыми переменными может привести к ORA-01036: illegal variable name/number. Парадокс в том, что тот же апдейт может проходить, если выполнять его по одной строке через execute(). Ниже — краткое объяснение, почему так происходит, и как правильно сопоставить плейсхолдеры SQL с вашими данными в oracledb (Python 3.9, oracledb 1.4.1).

Демонстрация проблемы

Проблемный сценарий использует пакетное выполнение с позиционными привязками. В тексте SQL один из плейсхолдеров заключён в кавычки, а порядок значений в кортежах не совпадает с порядком привязок.

df_pairs = src_df[["CONTRACT_NUM", "DOCID"]].copy()
cur = dbh.cursor()
batch_vals = [tuple(x) for x in df_pairs.values]
print(batch_vals)
stmt = "UPDATE USERBOX_MIDDLE_OFFICE.rvp_doc_partner_to_archive SET DOCID = :2    WHERE CONTRACT_NUM = ':1'"
cur.executemany(stmt, batch_vals)
rows_changed = cur.rowcount
dbh.commit()
cur.close()

DatabaseError: ORA-01036: illegal variable name/number

Почему это падает

Корневая причина — некорректное использование связываемых переменных. Плейсхолдер ':1' в SQL взят в кавычки, из‑за чего он превращается в строковый литерал, а не в привязку. В результате Oracle не может подставить в него значение. Есть и тонкость: цифры в именах плейсхолдеров не связаны с индексами кортежа; если вы не используете именованные привязки, порядок в SQL определяет позиционное связывание. В ошибочном запросе DOCID присваивается через :2, а в WHERE используется ':1', при том что входные кортежи имеют вид (CONTRACT_NUM, DOCID). Несоответствие порядка значений и позиционных плейсхолдеров даёт путаницу и приводит к ошибке. Для сравнения, поштучный путь с execute() срабатывает, потому что значения вставляются напрямую в текст SQL (со всеми кавычками для строк), и привязки там не участвуют.

Решение и исправленный код

Уберите кавычки у плейсхолдеров и приведите порядок данных к порядку привязок в SQL. Поскольку в запросе мы присваиваем DOCID и фильтруем по CONTRACT_NUM, первое значение в каждом кортеже должно соответствовать DOCID, а второе — CONTRACT_NUM.

# Переупорядочьте столбцы в соответствии с позициями привязок в SQL
pairs_reordered = src_df[["DOCID", "CONTRACT_NUM"]].copy()
cur2 = dbh.cursor()
bulk_params = [tuple(v) for v in pairs_reordered.values]
print(bulk_params)
sql_fixed = """
UPDATE USERBOX_MIDDLE_OFFICE.rvp_doc_partner_to_archive
SET    DOCID = :1
WHERE  CONTRACT_NUM = :2"""
cur2.executemany(sql_fixed, bulk_params)
affected_total = cur2.rowcount
dbh.commit()
cur2.close()

Так первое место привязки соответствует DOCID, а второе — CONTRACT_NUM. Строковые значения не нужно оборачивать в апострофы — драйвер сделает это сам. Важно лишь не брать в кавычки сам плейсхолдер и передавать значения в кортежах в том порядке, который указан в запросе.

Почему это важно

Понимание механики связываемых переменных критично для надёжных пакетных операций. Разница между позиционным связыванием и фактическим порядком ваших входных данных напрямую определяет, как executemany() подставляет значения в SQL. Осознание того, что числа в плейсхолдерах не связаны с индексами кортежей, убережёт от ложных предположений. И знание о том, что плейсхолдеры нельзя брать в кавычки, помогает избежать скрытых багов и ошибок во время выполнения.

Что запомнить

Если executemany() выдаёт ORA-01036, а поштучный execute() вроде бы работает, проверьте два момента. Во‑первых, плейсхолдеры не должны быть в кавычках. Во‑вторых, порядок столбцов в ваших данных должен соответствовать порядку позиционных привязок в SQL, либо используйте явные именованные привязки. С этими поправками пакетные обновления в Python 3.9 и oracledb 1.4.1 работают предсказуемо и стабильно.

Статья основана на вопросе на StackOverflow от Oleg Parunev и ответе от MT0.