2

我有 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

4 回答 4

3

我想您已经知道这一点,但让我们从一条教条开始:您指定该字段必须是唯一的,因此您必须让数据库检查唯一性或处理错误,以免发生这种情况。

检查唯一性:

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

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

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

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

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

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

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

于 2012-06-30T13:00:07.177 回答
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()若有。
于 2012-07-02T17:02:05.413 回答
1

您可能需要考虑按照 PostgreSQL 文档中的这个示例的方式编写一个函数。

于 2012-06-30T16:47:43.133 回答
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 遇到错误,我将 2375 记录的主键“pk”存储在 failed_records 中,然后我从 2000 重新提交到 2375。这似乎比一个一个提交要快得多。

于 2012-07-27T06:06:23.217 回答