SQLAlchemy IntegrityError和批量数据导入

时间:2012-05-14 15:04:08

标签: python sqlalchemy

我将几条10k记录插入到具有REF完整性规则的数据库中。遗憾的是,一些数据行是重复的(因为它们已经存在于数据库中)。在插入之前检查数据库中每一行的存在是太昂贵了所以我打算继续处理SQLAlchemy抛出的IntegrityError异常,记录错误然后继续。

我的代码看起来像这样:

# 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

我在上面做的(隐式)假设是SQLAlchemy没有将多个插入滚动到一个事务中。如果我的假设是错误的,则意味着如果发生IntegrityError,则插入的其余部分将被中止。任何人都可以确认伪代码"模式"以上将按预期工作 - 或者由于抛出IntegrityError异常,我最终会丢失数据吗?

此外,如果有人有更好的想法,我会有兴趣听到它。

3 个答案:

答案 0 :(得分:1)

它可能会像这样工作,如果您之前没有开始任何交易,因为在这种情况下,sqlalchemy的autocommit feature将启动。但您应该如链接中所述明确设置。

答案 1 :(得分:0)

当我解析ASCII数据文件以将数据导入表格时,我也遇到了这个问题。问题是我本能地和直觉地希望SQLAlchemy跳过重复的行,同时允许唯一的数据。或者可能是由于当前的SQL引擎而导致随机错误被抛出,例如不允许使用unicode字符串。

但是,此行为超出了SQL接口定义的范围。 SQL API,因此SQLAlchemy只能理解事务和提交,并且不考虑这种选择性行为。此外,依赖自动提交功能听起来很危险,因为插入在异常之后停止,剩下的是其余的数据。

我的解决方案(我不确定它是否是最优雅的解决方案)是处理循环中的每一行,捕获并记录异常,并在最后提交更改。

假设您以某种方式获取了列表列表中的数据,即列列表的行列表。然后你读取循环中的每一行:

get

为了最大限度地提高此操作的速度,您应该禁用自动提交。我在SQLite中使用此代码,它仍然比仅使用> # My data > x <- data.frame(a=1:3) > y <- data.frame(a=4:6) > # Save as RDA > save(x, file = "x.rda") > save(y, file = "y.rda") > files <- c("x.rda", "y.rda") > data <- lapply(lapply(files, load), get) > data [[1]] a 1 1 2 2 3 3 [[2]] a 1 4 2 5 3 6 的旧版本慢3-5倍,即使禁用了自动提交。 (我移植到SQLAlchemy的原因是能够将它与MySQL一起使用。)

它不是最优雅的解决方案,因为它不如SQLite的直接接口快。如果我在不久的将来分析代码并找到瓶颈,我将用解决方案更新这个答案。

答案 2 :(得分:0)

几乎没有办法告诉 sql 引擎做一个 bulk insert on duplicate ignore 动作。但是,我们可以尝试在 python 端做一个后备解决方案。如果您的重复项没有以非常糟糕的方式分布*,这几乎会获得两全其美的好处。

try:
    # by very bad, I mean what if each batch of the items contains one duplicate
    session.bulk_insert_mappings(mapper, items)
    session.commit()
except IntegrityError:
    logger.info("bulk inserting rows failed, fallback to one by one")
    for item in items:
        try:
            session.execute(insert(mapper).values(**item))
            session.commit()
        except SQLAlchemyError:
            logger.exception("Error inserting item: %s", item)