bigywater.tech
Django · 34

exists()、count()、len() 的区别

判断「有没有」和「有几个」有四种写法,差距有多大?这篇在 20 万行的表上把 exists、count、len、bool 的耗时、内存和 SQL 都量出来。

0.22 ms vs 615 ms
20 万行:qs.exists() vs len(qs)
2.48 ms
qs.count() 的耗时(SELECT COUNT(*))
137 MiB
len(qs) / bool(qs) 构造模型对象的峰值内存

量一量

实测 · 本机执行输出
qs.exists()                                             0.22 ms   峰值内存    0.01 MiB   → True
bool(qs)(if qs:)                                      616.10 ms   峰值内存  137.42 MiB   → True
qs.count()                                              2.48 ms   峰值内存    0.02 MiB   → 199900
len(qs)                                               614.92 ms   峰值内存  137.42 MiB   → 199900
len(list(qs.values_list("a", flat=True)))              58.89 ms   峰值内存   20.10 MiB   → 199900

耗时取 3 次里最快的,内存另开一次用 tracemalloc 量峰值。

为什么

实测 · 本机执行输出
exists()       SELECT 1 AS "a" FROM "lab_row" WHERE "lab_row"."a" >= 100 LIMIT 1
count()        SELECT COUNT(*) AS "__count" FROM "lab_row" WHERE "lab_row"."a" >= 100
len()          SELECT "lab_row"."id", "lab_row"."a", "lab_row"."b", "lab_row"."d" FROM "lab_row" WHERE "lab_row"."a" >= 100
bool()         SELECT "lab_row"."id", "lab_row"."a", "lab_row"."b", "lab_row"."d" FROM "lab_row" WHERE "lab_row"."a" >= 100

exists() 让数据库找到第一行就停;count() 让数据库数;len() / bool() 是先把整个 QuerySet 求值。这里的差别是「数据库做」还是「Python 做」。

缓存的影响

实测 · 本机执行输出
全新 qs:count() 查询 1 次;然后 len(qs) 又查询 1 次(填充缓存);之后 count/exists/len 再查询 0 次

已经求值过的 QuerySet,len() 是免费的;所以「后面反正要遍历,顺便判断」时可以直接用 len() / bool(),不要多写一次 exists()(第 27 篇)。

分步演示:四种写法

exists / count / len
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite,20 万行,a 列有索引;条件 a >= 100
    • 没有验证:PostgreSQL 上 COUNT(*) 对大表的代价(全表扫描)与近似计数、MySQL InnoDB 的 COUNT、分页里的 count(第 49 篇)
    京ICP备2026067502号-1京公网安备11010502063487号