Django bulk_create,忽略导致IntegrityError的行?

Mei*_*ham 36 django postgresql bulk-load

我使用bulk_create将数千或行加载到postgresql数据库中.不幸的是,有些行导致IntegrityError并停止bulk_create进程.我想知道是否有办法告诉django忽略这些行并尽可能多地保存批次?

Ces*_*ssa 36

现在可以在Django 2.2上实现

Django 2.2 从文档中ignore_conflicts为该bulk_create方法添加了一个新选项:

在支持它的数据库上(除了PostgreSQL <9.5和Oracle之外的所有数据库),将ignore_conflicts参数设置为True会使数据库忽略无法插入任何未通过限制的行(例如重复的唯一值).启用此参数将禁用在每个模型实例上设置主键(如果数据库通常支持它).

例:

Entry.objects.bulk_create([
    Entry(headline='This is a test'),
    Entry(headline='This is only a test'),
], ignore_conflicts=True)
Run Code Online (Sandbox Code Playgroud)

  • 使用此技术时,您是否获得有关无法插入哪些行的返回信息? (6认同)
  • 看起来,如果打开该选项,几乎不会对性能产生影响。干得好,姜戈!并感谢您的回答... (4认同)
  • 评论中已经提到了这一点,但值得重复,因为文档的措辞有点令人困惑......它确实在数据库的行中设置了主键,您只是无法将其返回到该对象的版本上它返回... (2认同)

Cra*_*ger 8

(注意:我不使用Django,因此可能有更合适的框架特定答案)

Django不可能通过简单地忽略INSERT失败来实现这一点,因为PostgreSQL在第一个错误时中止了整个事务.

Django需要以下方法之一:

  1. INSERT 单独一个事务中的每一行并忽略错误(非常慢);
  2. SAVEPOINT在每个插入之前创建一个(可能有缩放问题);
  3. 仅当行尚不存在时才使用过程或查询(复杂且缓慢); 要么
  4. COPY将数据批量插入或(更好)到TEMPORARY表中,然后将其合并到主表服务器端.

类似upsert的方法(3)似乎是一个好主意,但是upsert和insert-if-not-exists是非常复杂的.

就个人而言,我会采取(4):我可能会批量插入一个新的单独的表,UNLOGGED或者TEMPORARY,然后我会运行一些手动SQL来:

LOCK TABLE realtable IN EXCLUSIVE MODE;

INSERT INTO realtable 
SELECT * FROM temptable WHERE NOT EXISTS (
    SELECT 1 FROM realtable WHERE temptable.id = realtable.id
);
Run Code Online (Sandbox Code Playgroud)

LOCK TABLE ... IN EXCLUSIVE MODE防止从导致与上面的说法,未能完成插入冲突创建行并发插入.它并不能防止并发SELECTS,只是SELECT ... FOR UPDATE,INSERT,UPDATEDELETE,所以从表中读取进行正常.

如果你不能阻止并发写入时间过长你也可以使用一个可写的CTE从复制行范围temptablerealtable如果它失败了,重试每个块.


Iva*_*van 7

一种不涉及手动 SQL 和临时表的快速解决方法是尝试批量插入数据。如果失败,则恢复为串行插入。

objs = [(Event), (Event), (Event)...]

try:
    Event.objects.bulk_create(objs)

except IntegrityError:
    for obj in objs:
        try:
            obj.save()
        except IntegrityError:
            continue
Run Code Online (Sandbox Code Playgroud)

如果你有很多很多错误,这可能不是那么有效(你会花更多的时间连续插入而不是批量插入),但我正在处理一个几乎没有重复的高基数数据集,所以这解决了我的大部分问题问题。