我在一个柱子上有一个独特的约束表,如:
CREATE TABLE entity (
id INT NOT NULL AUTO_INCREMENT,
zip_code INT NOT NULL,
entity_url VARCHAR(255) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY ix_uniq_zip_code_entity_url (zip_code, entity_url)
);
和相应的SQLAlchemy模型。我添加了很多记录,并且不想在每条记录之后提交会话。我的假设是,最好多次拨打session.add(new_record)
,一次session.commit()
。
但是在添加新记录时,我可以获得IntegrityError
,因为违反了约束。这是正常的情况,我想跳过这样的记录插入。但看起来我只能恢复整个交易。
此外,我不想添加另一个复杂的检查"从数据库获取所有记录,其中[...]中的zip_code和[...]中的entity_url然后从records_to_insert"中删除匹配的数据。
是否可以设置违反约束的SQLAlchemy drop记录?
答案 0 :(得分:4)
我的假设是,最好多次拨打
session.add(new_record)
,一次session.commit()
。
您可能想重新考虑这个假设。批量处理大量记录通常适用于多次提交 - 如果你有10k记录并且你的代码在9,999号引发异常怎么办?你将被迫重新开始。这里的核心问题是,在没有其他记录的情况下,其中一条记录是否存在于数据库中是否有意义。如果确实如此,那么每个条目的提交都没有问题(除了性能问题)。在这种情况下,您只需捕获IntegrityError并调用session.rollback()
继续记录列表。
无论如何,类似的问题是asked on the SQLA mailing list并由图书馆的创建者迈克拜耳回答。他建议你自己从新记录列表中删除重复项,因为这很容易用字典或集合。这可以像字典理解一样简单:
new_entities = { (entity['zip_code'], entity['url']): entity for entity in new_entities}
(这会将最后看到的副本选为要添加到数据库的副本。)
另请注意,他使用SQLAlchemy核心库来执行插入,而不是使用ORM的session.add()
方法:
sess.execute(Entry.__table__.insert(), params=inserts)
如果您处理大量记录(例如,他的示例中有100,000条记录),这是一个更快的选择。
答案 1 :(得分:0)
如果您决定逐行插入记录,则可以在插入之前检查它是否已存在。这可能是也可能不是更优雅和有效:
def record_exists(session, some_id):
return session.query(exists().where(YourEntity.id == some_id)).scalar()
for item in items:
if not record_exists(session, item.some_id):
session.add(record)
session.flush()
else:
print "Already exists, skipping..."
session.commit()