У меня есть код, который записывает Scrap скребущие данные на SQL-сервер db. Элементы данных состоят из некоторых основных данных о гостинице (имя, адрес, рейтинг ..) и некоторый список номеров со связанными данными (цена, занятость и т. Д.). Там могут быть несколько нитей сельдерея и несколько серверов, на которых выполняется этот код, и одновременно записывать в db разные элементы. Я сталкиваюсь с ошибками тупиковых, как:Ошибки SQL-сервера, pyobbc и тупика
[Failure instance: Traceback: <class 'pyodbc.ProgrammingError'>:
('42000', '[42000] [FreeTDS][SQL Server]Transaction (Process ID 62)
was deadlocked on lock resources with another process and has been
chosen as the deadlock victim. Rerun the transaction. (1205) (SQLParamData)')
кодом, который на самом деле делает вставки/обновления схематично выглядит следующим образом:
1) Check if hotel exists in hotels table, if it does update it, else insert it new.
Get the hotel id either way. This is done by `curs.execute(...)`
2) Python loop over the hotel rooms scraped. For each room check if room exists
in the rooms table (which is foreign keyed to the hotels table).
If not, then insert it using the hotel id to reference the hotels table row.
Else update it. These upserts are done using `curs.execute(...)`.
Это немного сложнее, чем это на практике, но это показывает, что код Python использует несколько curs.executes
до и во время цикла.
Если вместо воссоздания данных, описанных выше, я генерирую одну большую команду SQL, которая делает то же самое (проверяет отель, поднимает его, записывает идентификатор во временную переменную, для каждой комнаты проверяет, существует ли upserts против hotel id var и т. д.), затем сделать только один curs.execute(...)
в коде python, тогда я больше не вижу ошибки взаимоблокировки.
Однако я не совсем понимаю, почему это имеет значение, и я не совсем уверен, что безопасно запускать большие SQL-блоки с несколькими SELECT, INSERTS, UPDATES в одном pyodbc curs.execute
. Поскольку я понимаю, что pyodbc предполагает обрабатывать только отдельные операторы, однако он работает, и я вижу, что мои таблицы заполняются без ошибок взаимоблокировки.
Тем не менее, кажется, невозможно получить какой-либо результат, если я делаю такую большую команду. Я попытался объявить переменную @output_string
и записывать различные вещи к нему (делали мы должны вставить или обновить отель, например), прежде чем, наконец, SELECT @output_string as outputstring
, но делать выборку после выполнения в pyodbc всегда терпит неудачу с
<class 'pyodbc.ProgrammingError'>: No results. Previous SQL was not a query.
Эксперименты в пределах оболочка предложить pyodbc игнорирует все после первого заявления:
In [11]: curs.execute("SELECT 'HELLO'; SELECT 'BYE';")
Out[11]: <pyodbc.Cursor at 0x7fc52c044a50>
In [12]: curs.fetchall()
Out[12]: [('HELLO',)]
Так что, если первое утверждение не является запрос вы получите эту ошибку:
In [13]: curs.execute("PRINT 'HELLO'; SELECT 'BYE';")
Out[13]: <pyodbc.Cursor at 0x7fc52c044a50>
In [14]: curs.fetchall()
---------------------------------------------------------------------------
ProgrammingError Traceback (most recent call last)
<ipython-input-14-ad813e4432e9> in <module>()
----> 1 curs.fetchall()
ProgrammingError: No results. Previous SQL was not a query.
Тем не менее, за исключением невозможности получить мой @output_string
, мой реальный «большой запрос», состоящий из нескольких выборок, обновлений, вставок, фактически работает и заполняет несколько таблиц в db.
Тем не менее, если я пытаюсь что-то вроде
curs.execute('INSERT INTO testX (entid, thecol) VALUES (4, 5); INSERT INTO testX (entid, thecol) VALUES (5, 6); SELECT * FROM testX; '
...:)
Я вижу, что обе строки были вставлены в таблицу tableX
, даже последующее curs.fetchall()
терпит неудачу с «Предыдущий SQL не был запрос.» ошибка, поэтому кажется, что pyodbc execute выполняет все ... не только первый оператор.
Если я могу доверять этому, то моя главная проблема заключается в том, как получить некоторый результат для ведения журнала.
EDIT Установка autocommit=True
в dbargs, кажется, чтобы предотвратить ошибки тупиковыми, даже с несколькими curs.executes. Но почему это исправить?
Выполняется ли ваша «большая команда SQL» (предположительно, содержащая несколько операторов) с помощью 'SET NOCOUNT ON;'? –
@GordThompson Нет, это началось с «BEGIN» и заканчивается «END;», внутри него находятся несколько других блоков с «BEGIN, END», «IF/ELSE», «SELECTS», «INSERT», «UPDATES», , – fpghost