在提交期间删除错误查询

时间:2012-06-30 12:28:08

标签: python postgresql sqlalchemy

我有postgresql db,我正在更新大约100000条记录。我使用session.merge()来插入/更新每条记录,并在每1000条记录后执行一次提交。

i=0
for record in records:
    i+=1
    session.merge(record)
    if i%1000 == 0:
        session.commit()

此代码工作正常。在我的数据库中,我有一个带有UNIQUE字段的表,并且我插入了一些重复的记录。发生这种情况时会抛出错误,说该字段不是唯一的。由于我一次插入1000条记录,因此回滚不会帮助我跳过这些记录。有什么办法我可以跳过session.merge()的重复记录(除了解析所有的记录,当然找到重复的记录)?

4 个答案:

答案 0 :(得分:3)

我想你已经知道了这一点,但让我们从一个教条开始:你指定该字段需要是唯一的,所以你必须让数据库检查唯一性或处理错误,不要让它发生

检查唯一性:

if value not in database:
    session.add(value)
session.commit()

不检查唯一性并捕获异常。

try:
    session.add(value)
    session.commit()
except IntegrityError:
    session.rollback()

第一个有竞争条件。我倾向于使用第二种模式。

现在,回到实际问题,如果你想确保数据库中某列的唯一性,那么显然你将不得不让数据库确保加载值的实际唯一性,或者让数据库给你一个错误,你处理它。

这显然比向会话中添加100k对象并将它们全部提交要慢得多,但这就是数据库的工作方式。

您可能需要考虑按摩正在数据库外部加载的数据,并在尝试加载数据之前,以确保唯一性。这样,当您加载它时,您可以放弃检查唯一性的需要。使用命令行工具非常容易,例如,您从csv或文本文件加载。

答案 1 :(得分:2)

你可以使用SAVEPOINT进行“部分回滚”,SQLAlchemy通过begin_nested()公开它。你可以这样做:

for i, record in enumerate(records):
    try:
        with session.begin_nested():
            session.merge(record)
    except:
        print "Skipped record %s" % record
    if not i % 1000:
        session.commit()

上述说明:

  1. 在python中,我们从不做“i = i + 1”的事情。使用enumerate()
  2. with session.begin_nested():begin_nested()相同,如果没有例外则为commit(),如果是,则为rollback()

答案 2 :(得分:1)

您可能需要考虑从PostgreSQL文档中的this example开始编写函数。

答案 3 :(得分:0)

这是最适合我的选项,因为具有重复唯一键的记录数量很少。

def update_exception(records, i, failed_records):
    failed_records.append(records[i]['pk'])
    session.rollback()
    start_range = int(round(i/1000,0) * 1000)
    for index in range(start_range, i+1):
        if records[index]['pk'] not in failed_records:
            ins_obj = Model()
            try:
                session.merge(ins_obj)
            except:
                failed_records.append(json_data[table_name][index-1]['pk'])
                pass

说,如果我在2375处遇到错误,我会在fail_records中存储2375记录的主键'pk',然后我会从2000到2375重新发送。它似乎比一个接一个地提交要快得多。