2012-05-14 30 views
6

Tôi đang chèn nhiều bản ghi 10k vào cơ sở dữ liệu với quy tắc toàn vẹn REF. Một số hàng dữ liệu không may bị trùng lặp (ở chỗ chúng đã tồn tại trong cơ sở dữ liệu). Nó sẽ là quá đắt để kiểm tra sự tồn tại của mỗi hàng trong cơ sở dữ liệu trước khi chèn nó vì vậy tôi có ý định tiến hành bằng cách xử lý các trường hợp ngoại lệ IntegrityError được tạo bởi SQLAlchemy, ghi lại lỗi và sau đó tiếp tục.SQLAlchemy IntegrityError và nhập dữ liệu hàng loạt

Mã của tôi sẽ giống như thế này:

# 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 (implicit) giả định tôi đang làm trên là SQLAlchemy không lăn nhiều chèn vào ONE giao dịch. Nếu giả định của tôi là sai thì nó có nghĩa là nếu một IntegrityError xảy ra, phần còn lại của chèn bị hủy bỏ. Bất cứ ai có thể xác nhận nếu giả "mô hình" ở trên sẽ làm việc như mong đợi - hoặc tôi sẽ kết thúc mất dữ liệu như là kết quả của ném ngoại lệ IntegrityError?

Ngoài ra, nếu có ai có ý tưởng tốt hơn về việc này, tôi sẽ muốn nghe điều đó.

Trả lời

1

nó có thể hoạt động như thế này, nếu bạn không bắt đầu bất kỳ giao dịch nào trước đây, như trong trường hợp này, số autocommit feature của sqlalchemy sẽ được khởi động. Tuy nhiên, bạn nên đặt rõ ràng như được mô tả trong liên kết.

0

Tôi cũng gặp sự cố này khi tôi phân tích cú pháp tệp dữ liệu ASCII để nhập dữ liệu vào bảng. Vấn đề là tôi theo bản năng và trực giác muốn SQLAlchemy bỏ qua các hàng trùng lặp trong khi cho phép các dữ liệu duy nhất. Hoặc nó có thể là trường hợp một lỗi ngẫu nhiên được ném với một hàng, do công cụ SQL hiện tại, chẳng hạn như chuỗi unicode không được phép.

Tuy nhiên, hành vi này nằm ngoài phạm vi định nghĩa của giao diện SQL. Các API SQL và do đó SQLAlchemy chỉ hiểu các giao dịch và các cam kết, và không tính đến hành vi chọn lọc này. Hơn nữa, nó có vẻ nguy hiểm để phụ thuộc vào tính năng tự động, vì chèn tạm dừng sau khi ngoại lệ, để lại phần còn lại của dữ liệu.

Giải pháp của tôi (mà tôi không chắc đó là giải pháp thanh lịch nhất) là xử lý mọi dòng trong một vòng lặp, bắt và ghi ngoại lệ, và cam kết thay đổi vào cuối cùng.

Giả sử bạn bằng cách nào đó đã mua dữ liệu trong danh sách danh sách, tức là danh sách các hàng là danh sách giá trị cột. Sau đó, bạn đọc từng hàng trong một vòng lặp:

# 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() 

Để tối đa hóa tốc độ trong thao tác này, bạn nên tắt tự động. Tôi đang sử dụng mã này với SQLite và nó vẫn còn 3-5 lần chậm hơn so với phiên bản cũ của tôi chỉ sử dụng sqlite3, ngay cả với tự động vô hiệu hóa. (Lý do tôi chuyển sang SQLAlchemy là để có thể sử dụng nó với MySQL.)

Đây không phải là giải pháp thanh lịch nhất theo nghĩa là nó không nhanh như giao diện trực tiếp với SQLite. Nếu tôi cấu hình mã và tìm thấy nút cổ chai trong tương lai gần, tôi sẽ cập nhật câu trả lời này với giải pháp.

Các vấn đề liên quan