querysetの存在確認にexists()とcount()はどちらの方が早い?
今回はDjangoのquerysetの存在確認にexists()とcount()はどちらの方が早いのかについて調査した結果について書きたいと思います。
先にネタバレしますが、count()よりもexists()の方がパフォーマンスが良いのでquerysetの存在確認のためには素直にexists()を使用すると良いでしょう。
exists()を使用した場合はquerysetの件数が増えてもほぼパフォーマンスが変わらないです。
動作環境としてはDjangoのバージョンは3.0.1を使用しており、Pythonのバージョンは3.7.3を使用しております。
Contents
querysetの存在確認にexists()とcount()はどちらの方が早い?
querysetのexists()とcount()どちらがパフォーマンス的に良いのか調べるために次のソースを用意しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from book.models import Book import time def count_test(): bookAll = Book.objects.all() print(bookAll) start = time.time() for i in range(10000): if bookAll.count() > 0: pass process_time = time.time() - start print(process_time) def exists_test(): bookAll = Book.objects.all() print(bookAll) start = time.time() for i in range(10000): if bookAll.exists(): pass process_time = time.time() - start print(process_time) |
count_test()はcount()を使って1万回querysetの存在確認してそのタイムを計る関数です。
対して、exists_test()はexists()を使って1万回querysetの存在確認してそのタイムを計る関数です。
次のBookモデルを使ってタイムを計測します。
1 2 3 4 5 6 7 |
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=120) # max_length=required author = models.CharField(max_length=120) # max_length=required price = models.DecimalField(decimal_places=2, max_digits=100, null=True) |
初期条件として、Bookオブジェクトが一個だけある場合を考えてみます。
1 2 3 4 |
>>> Book.objects.all() <QuerySet [<Book: Book object (1)>]> >>> Book.objects.all().count() 1 |
手始めにcount_test()を三回実行してみます。
count_test()を三回実行した結果はこちらです。
1 2 3 |
>>> count_test() <QuerySet [<Book: Book object (1)>]> 2.16890287399292 |
1 2 3 |
>>> count_test() <QuerySet [<Book: Book object (1)>]> 2.1642260551452637 |
1 2 3 |
>>> count_test() <QuerySet [<Book: Book object (1)>]> 2.175992012023926 |
count_test()の実行時間は約2秒ちょっとという結果になりました。
一方、exists_test()を三回実行してみます。
exists_test()を三回実行した結果は次の通りです。
1 2 3 |
>>> exists_test() <QuerySet [<Book: Book object (1)>]> 1.8513872623443604 |
1 2 3 |
>>> exists_test() <QuerySet [<Book: Book object (1)>]> 1.8344120979309082 |
1 2 3 |
>>> exists_test() <QuerySet [<Book: Book object (1)>]> 1.8385632038116455 |
exists_test()は2秒を切るという結果になりました。
現時点で結果だけ見るならばcount()よりもexists()の方がパフォーマンスが良いということになります。
querysetの件数を増やしてみる
では、先ほどの実験ではquerysetの総数が1個だけでしたが、querysetの件数を増やしてみた場合はどうなるのでしょうか。
querysetの件数を1件ではなく100万件に増やして実験してみます。
まず100万件に増やしたquerysetに対してcount_test()を三回実行した結果はこちらです。
1 2 3 |
>>> count_test() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>, <Book: Book object (7)>, <Book: Book object (8)>, <Book: Book object (9)>, <Book: Book object (10)>, <Book: Book object (11)>, <Book: Book object (12)>, <Book: Book object (13)>, <Book: Book object (14)>, <Book: Book object (15)>, <Book: Book object (16)>, <Book: Book object (17)>, <Book: Book object (18)>, <Book: Book object (19)>, <Book: Book object (20)>, '...(remaining elements truncated)...']> 41.07088780403137 |
1 2 3 |
>>> count_test() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>, <Book: Book object (7)>, <Book: Book object (8)>, <Book: Book object (9)>, <Book: Book object (10)>, <Book: Book object (11)>, <Book: Book object (12)>, <Book: Book object (13)>, <Book: Book object (14)>, <Book: Book object (15)>, <Book: Book object (16)>, <Book: Book object (17)>, <Book: Book object (18)>, <Book: Book object (19)>, <Book: Book object (20)>, '...(remaining elements truncated)...']> 40.66119694709778 |
1 2 3 |
>>> count_test() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>, <Book: Book object (7)>, <Book: Book object (8)>, <Book: Book object (9)>, <Book: Book object (10)>, <Book: Book object (11)>, <Book: Book object (12)>, <Book: Book object (13)>, <Book: Book object (14)>, <Book: Book object (15)>, <Book: Book object (16)>, <Book: Book object (17)>, <Book: Book object (18)>, <Book: Book object (19)>, <Book: Book object (20)>, '...(remaining elements truncated)...']> 42.1782021522522 |
実行時間がめちゃくちゃ増えていますね。
count_test()を一回実行するために40秒をオーバーしています。
一方、exists_test()を実行した場合はどうでしょうか。
exists_test()を三回実行した結果はこちらです。
1 2 3 |
>>> exists_test() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>, <Book: Book object (7)>, <Book: Book object (8)>, <Book: Book object (9)>, <Book: Book object (10)>, <Book: Book object (11)>, <Book: Book object (12)>, <Book: Book object (13)>, <Book: Book object (14)>, <Book: Book object (15)>, <Book: Book object (16)>, <Book: Book object (17)>, <Book: Book object (18)>, <Book: Book object (19)>, <Book: Book object (20)>, '...(remaining elements truncated)...']> 1.8246846199035645 |
1 2 3 |
>>> exists_test() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>, <Book: Book object (7)>, <Book: Book object (8)>, <Book: Book object (9)>, <Book: Book object (10)>, <Book: Book object (11)>, <Book: Book object (12)>, <Book: Book object (13)>, <Book: Book object (14)>, <Book: Book object (15)>, <Book: Book object (16)>, <Book: Book object (17)>, <Book: Book object (18)>, <Book: Book object (19)>, <Book: Book object (20)>, '...(remaining elements truncated)...']> 1.7771358489990234 |
1 2 3 |
>>> exists_test() <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>, <Book: Book object (5)>, <Book: Book object (6)>, <Book: Book object (7)>, <Book: Book object (8)>, <Book: Book object (9)>, <Book: Book object (10)>, <Book: Book object (11)>, <Book: Book object (12)>, <Book: Book object (13)>, <Book: Book object (14)>, <Book: Book object (15)>, <Book: Book object (16)>, <Book: Book object (17)>, <Book: Book object (18)>, <Book: Book object (19)>, <Book: Book object (20)>, '...(remaining elements truncated)...']> 1.9234497547149658 |
exists_test()はquerysetの件数が増えてもパフォーマンスが良いままです。
というよりquerysetの件数を100万件にしても、querysetの件数が1件の時とパフォーマンスがほぼ変わらないように見えます。
querysetのexists()は最初の一件のみで評価している
querysetのexists()を使ってもquerysetの件数が1件の時と100万件の時でパフォーマンスにほとんど変化が見られないのは内部のロジックのおかげです。
Djangoの公式ドキュメントによると、querysetのexists()は最初の1件のみを評価しております。
querysetの総数が1件だけだろうが100万件あろうが最初の1件のみを見るので実行時間はほぼ変わらないようですね。
querysetの存在確認に関してはcount()よりもコードとして見た目も良くquerysetの数が増えたときにパフォーマンスがほぼ変わらないexists()を使用すると良いでしょう。
終わりに
今回はDjangoのquerysetの存在確認にexists()とcount()はどちらの方が早いのかについて調査した結果について書きました。
PythonやPythonを使ったライブラリやフレームワークのパフォーマンスの調査に興味があり、様々なコーディング方法の違いによる最適化の調査を行っております。
リストにまとめてあるので、よろしければ覗いてみてください。
Pythonとそのライブラリやフレームワークのパフォーマンス調査結果リスト
ディスカッション
コメント一覧
まだ、コメントがありません