2012-05-14 12 views
6

Wstawiam kilka rekordów 10k do bazy danych z regułami integralności REF. Niektóre wiersze danych są niestety duplikowane (ponieważ już istnieją w bazie danych). Byłoby zbyt drogie, aby sprawdzić istnienie każdego wiersza w bazie danych przed jego wstawieniem, dlatego zamierzam kontynuować obsługę wyjątków IntegrityError, które zostały zgłoszone przez SQLAlchemy, rejestrowanie błędu i kontynuowanie.SQLAlchemy IntegrityError i hurtowy import danych

Mój kod będzie wyglądał mniej więcej tak:

# establish connection to db etc. 

tbl = obtain_binding_to_sqlalchemy_orm() 
datarows = load_rows_to_import() 

try: 
    conn.execute(tbl.insert(), datarows) 
except IntegrityError as ie: 
    # eat error and keep going 
except Exception as e: 
    # do something else 

The (implicite) założenie Robię powyżej jest to, że SQLAlchemy nie toczy się wiele wkładek w ramach jednej transakcji. Jeśli moje założenie jest błędne, oznacza to, że jeśli wystąpi błąd IntegrityError, reszta wstawki zostanie przerwana. Czy ktokolwiek może potwierdzić, że powyższy "wzorzec" pseudokodu działa zgodnie z oczekiwaniami - czy stracę dane w wyniku odrzucenia wyjątków IntegrityError?

Ponadto, jeśli ktoś ma lepszy pomysł na zrobienie tego, będę zainteresowany, aby to usłyszeć.

Odpowiedz

1

może działać tak, jeśli wcześniej nie rozpoczęto żadnej transakcji, tak jak w tym przypadku zostanie uruchomiona funkcja sqlalchemy autocommit feature. Należy jednak jawnie ustawić ją zgodnie z opisem w łączu.

0

Również napotkałem ten problem podczas analizowania plików danych ASCII w celu zaimportowania danych do tabeli. Problem polega na tym, że instynktownie i intuicyjnie chciałem, aby SQLAlchemy pomijało duplikaty wierszy, jednocześnie zezwalając na unikalne dane. Lub może być przypadek, że losowy błąd jest zgłaszany w wierszu, ze względu na obecny silnik SQL, takich jak łańcuchy unicode nie są dozwolone.

Jednak takie zachowanie wykracza poza zakres definicji interfejsu SQL. SQL APIs, a zatem SQLAlchemy tylko rozumie transakcje i zatwierdza i nie uwzględnia tego selektywnego zachowania. Co więcej, używanie funkcji autocommit może być niebezpieczne, ponieważ wstawianie zatrzymuje się po wyjściu, pozostawiając resztę danych.

Moje rozwiązanie (którego nie mam pewności, czy jest to najbardziej elegancka) to przetwarzanie każdej linii w pętli, przechwytywanie i rejestrowanie wyjątków i zatwierdzanie zmian na samym końcu.

Zakładając, że w jakiś sposób pozyskano dane na liście list, tj. Listę wierszy, które są listami wartości kolumn. Następnie czytasz każdy wiersz w pętli:

# Python 3.5 
from sqlalchemy import Table, create_engine 
import logging 

# Create the engine 
# Create the table 
# Parse the data file and save data in `rows` 

conn = engine.connect() 
trans = conn.begin() # Disables autocommit 

exceptions = {} 
totalRows = 0 
importedRows = 0 

ins = table.insert() 

for currentRowIdx, cols in enumerate(rows): 
    try: 
     conn.execute(ins.values(cols)) # try to insert the column values 
     importedRows += 1 

    except Exception as e: 
     exc_name = type(e).__name__ # save the exception name 
     if not exc_name in exceptions: 
      exceptions[exc_name] = [] 
     exceptions[exc_name].append(currentRowIdx) 

    totalRows += 1 

for key, val in exceptions.items(): 
    logging.warning("%d out of %d lines were not imported due to %s."%(len(val), totalRows, key)) 

logging.info("%d rows were imported."%(importedRows)) 

trans.commit() # Commit at the very end 
conn.close() 

Aby zmaksymalizować prędkość w tej operacji, należy wyłączyć automatyczne zatwierdzanie. Używam tego kodu z SQLite i jest on wciąż 3-5 razy wolniejszy niż moja starsza wersja, używając tylko sqlite3, nawet z wyłączonym autocommitem. (Powodem przeniesienia do SQLAlchemy było móc go używać z MySQL.)

To nie jest najbardziej eleganckie rozwiązanie w tym sensie, że nie jest tak szybkie, jak bezpośredni interfejs do SQLite. Jeśli profiluję kod i znajduję wąskie gardło w najbliższej przyszłości, zaktualizuję tę odpowiedź za pomocą rozwiązania.

Powiązane problemy