Djangoでレコードの一括作成・更新を行う方法を紹介します。
基本的にDBに対するクエリが多くなると処理に時間がかかるので、できるだけ発行するクエリを減らすことが良いコードに繋がります。
Djangoで同じモデルに対して作成や更新を複数回行う場合に、1クエリで処理できる bulk_create と bulk_update があります。
関数の使い方と、使わない場合との処理時間の違いをまとめました。
なお、この記事で使うPython・Djangoのバージョンは以下のとおりです。
Python 3.7
Django 2.2.5
bulk_create
bulk_create はレコードを一括作成する関数です。
Django1系でもサポートされています。
bulk_createを使用しない場合
比較のために、ループ内でクエリを発行する場合の処理時間を計測しました。
例は、titleとpriceカラムを持つBookテーブルのレコードを1万件作成しています。
for i in range(10000):
Book.objects.create(title='タイトル'+str(i), price=i) #作成のクエリ発行
>> 65.8s
1万件作成するのに65.8秒かかりました。
bulk_createを使用した場合
同じレコード1万件を bulk_create を使用して作成すると、
add_books = [] # 作成クエリの配列
for i in range(10000):
book = Book(title='タイトル'+str(i), price=i)
add_books.append(book) # 作成クエリを配列に格納
Book.objects.bulk_create(add_books) # ここで追加のクエリ1つ発行
>> 2.4s
2.4秒で終わりました。(笑)
bulkを使う方が圧倒的に処理が速いです。
createやsaveと同様に作成するモデルインスタンスを作り、配列に格納して bulk_create でまとめて処理します。
bulk_update
bulk_update はレコードを一括更新する関数です。Django2.2からサポートされているのでご注意を!
bulk_updateを使用しない場合
比較のためにループ内でクエリを発行した場合を計測します。
例は、先ほど作成したBookレコードのpriceを全て100にするというものです。
books = Book.objects.all() # レコード1万件取得
for book in books:
book.price = 100
book.save() # 更新のクエリ発行
>> 90.6s
1万件更新するのに90.6秒かかりました。
bulk_updateを使用した場合
同じ更新を bulk_update を使用して行うと、
upd_books = [] # 更新クエリの配列
books = Book.objects.all() # レコード1万件取得
for book in books:
book.price = 100
upd_books.append(book) # 更新クエリを配列に格納
Book.objects.bulk_update(upd_books, fields=['price']) # ここで更新クエリ1つ発行
>> 3.8s
3.8秒で終わりました。
更新の場合も圧倒的に早いですね。
bulk_create と違いfieldsの配列に更新するカラムを指定する必要があります。
bulk_create 、 bulk_update 両方に引数 batch_size を指定でき、指定した数ごとにクエリを発行させることができます。
膨大な数の作成・更新の場合は、 batch_size を5000や10000にしておく方がパフォーマンス的に良いでしょう。