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