Как обрабатывать исключение с помощью executemany (MySQL и Python)

У меня есть сценарий python, использующий executemany для массовой вставки строк в таблицу MySQL. Данные извлекаются из разных API, поэтому время от времени появляются неожиданные данные, которые приводят к строке, вызывающей исключение.

Если я правильно понимаю - при вызове executemany с 1000 строками и одна из них проблематична - весь объем не вставляется.

Я хочу найти способ отправить 1000 записей и успешно загрузить те, которые не вызывают проблем. Так, например, если один из тысячи проблематичен, он не будет загружен, но все остальные 999 будут загружены.

Какая лучшая практика в этом отношении? Я подумываю перехватить исключение и создать запасной вариант, чтобы повторно отправить все 1000 один за другим, но похоже, что должен быть лучший способ достичь того же результата.

Совет?


person Aamit    schedule 04.12.2018    source источник
comment
какие исключения вы получаете? Что заставляет вас думать, что повторная попытка сработает?   -  person danblack    schedule 04.12.2018
comment
Если, например, проблематична только запись 364 из партии 1000 - я хотел бы повторить попытку вставки всех остальных 999 записей по отдельности. Поскольку я не знаю, какая запись вызвала исключение, а потому, что одно исключение вызывает сбой всего пакета, я думаю о повторении всех 1000 как отдельных операторов (выполнить вместо выполнения многих). Исключением может быть что угодно: пустое поле PK, неожиданная кодировка и т. Д.   -  person Aamit    schedule 04.12.2018
comment
да, повторная попытка сузит ошибку до минимального количества строк.   -  person danblack    schedule 04.12.2018


Ответы (2)


Выполнение инструкции INSERT OR IGNORE в начале вашего запроса Executemany позволит вам сделать именно это - он добавит только те значения, которые не приводят к ошибке.

Единственным недостатком является то, что вы больше не можете видеть, какая ошибка происходит. Например,

Исходная база данных:

('kaushik', 3)
('maria', 4)
('shreya', 38)

Запрос: (в Python)

listofnames = [
('kaushik', 3),
('maria', 4),
('jane', 56)
]

c.executemany("INSERT OR IGNORE INTO bob (name, number) VALUES (?,?)", 
listofnames)

Окончательный db:

('kaushik', 3)
('maria', 4)
('shreya', 38)
('jane', 56)
person Kaushik Donthi    schedule 22.07.2020

При вставке executemany объединяет все строки данных вместе и пытается вставить их все одной командой. Насколько мне известно, невозможно обработать исключение, вызванное одной неудачной вставкой, без разрушения всего пакета вставок. Если одна строка терпит неудачу, вся команда терпит неудачу.

Вот как это выглядит (пример взят из MySQL docs). Если вы скажете ему сделать это:

data = [
  ('Jane', date(2005, 2, 12)),
  ('Joe', date(2006, 5, 23)),
  ('John', date(2010, 10, 3)),
]
stmt = "INSERT INTO employees (first_name, hire_date) VALUES (%s, %s)"
cursor.executemany(stmt, data)

executemany сделает это:

INSERT INTO employees (first_name, hire_date)
VALUES ('Jane', '2005-02-12'), ('Joe', '2006-05-23'), ('John', '2010-10-03')

Если вы думаете, что это будет редкостью, ваша идея повторять попытки каждой вставки по отдельности сработает. Что-то вроде:

try:
    cursor.executemany(stmt, data)
except ___Error:  # fill in the blank
    for datum in data:
        try:
            cursor.execute(stmt, datum)
        except ___Error:
            # handle exception, eg print warning
            ...

Если вы думаете, что это будет частая проблема, то, вероятно, будет эффективнее отказаться от executemany и просто сделать следующее:

for datum in data:
    try:
        cursor.execute(stmt, datum)
    except ___Error:
        # handle exception, eg print warning
        ...
person ibonyun    schedule 14.10.2019