slothis · 日本語

DjangoのQuerySetで行数を数える時のcount()とlen()の違い

2020-12-05 · 更新日: 2026-08-11 · 原文(韓国語)

この記事は韓国語のオリジナル記事をAIの支援で翻訳したものです。 韓国語の原文を読む →
濃い緑色の背景に白い小文字で書かれたDjangoフレームワークの公式ロゴ

最近受けたコードレビューで、len()で行数を取得していた部分をcount()に変更してほしいというコメントをもらった。ORMとDjangoの初心者なのでStack Overflowをいくつか漁ってみたところ、行数を確認する時はなるべくcountを使って取得するのが推奨されているようだ。では、その理由が何なのか確認してみよう。

現在自分のサービスにいるUserの数を調べるとしよう。

(ユーザーが100万人、1000万人いるような環境でこういうことを遊び半分でやると、会社のDevOpsの先輩方に呼び出されるので注意しよう。)

qs = User.objects.all()\n\n# querysetのcount()を使用する方法がある。\ndef user_count_by_count():\n    return qs.count()\n\n# lenで確認する方法\ndef user_count_by_len():\n    return len(qs)

この時、count()の場合はRDBMS上でSELECT COUNT(*) FROM Userが実行され、そのカウント値がPythonに渡される。

len()の場合は、RDBMS上でSELECT * FROM Userを一度実行し、Pythonに渡された結果をlen()で数えることになる。

よく「行数を数える必要があるならcount()を使え」と言われる。その理由は、len()でカウントを代用しようとすると、フェッチする際に時間がO(N)だけかかるからだ。また、この結果をWebサーバーのメモリに格納する際にストレージにO(N)が発生し、コピーにかかる時間のせいで時間にもO(N)が追加で発生する。さらに加えて、len()が実行される時間も発生する。この2つのプロセスはインフラ環境によって異なるが、countの方が2倍ほど速いそうだ。

しかし、len()を使う方がcount()より有利な場合もある。

qs = User.objects.all()\n\ndef prefer_len_to_count():\n    # User情報の中にtelephoneフィールドがあると仮定し、その情報をリストにまとめる。\n    telephones = [p.telephone for p in qs]\n    # その情報で何か作業をして...\n    ...\n    # qsの個数を取得する(用途は各自で考えてみよう)\n    len(qs)

上記のようにQuerySetをフェッチして使用する状況であれば、lenの方がほんの少し有利だ。理由は、count()の場合はRDBMSに2回(フェッチで1回、count()で1回)アクセスしなければならないからだ。

DevOpsの先輩方に怒られそうな言葉だが、ものすごく多くの(この基準はインフラ状況によって大きく異なる)行を数えるのでなければ、そのままcountを使っても差し支えない

当然、テンプレートでもcountの使用が推奨される。

以下のcountの方が、

{{ some_queryset.count }}

以下のlenよりも推奨される。

{{ len(some_queryset) }}

参考: stackoverflow.com/questions/14327036/count-vs-len-on-a-django-queryset


#django #ジャンゴ