我有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()的重复记录(除了解析所有的记录,当然找到重复的记录)?
答案 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()
上述说明:
enumerate()
。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重新发送。它似乎比一个接一个地提交要快得多。