Durante uma discussão com um colega de trabalho sobre a otimização de consultas sobre trilhos, preciso provar minha teoria de que contar é mais rápido do que vazio. pois no primeiro método active record ele está fazendo diretamente sobre sql e no segundo está verificando o comprimento do array dado pelo resultado.
Meu ambiente
ruby 1.9.3-p448
rails 3.2.6
Então, para provar isso, fiz a próxima referência
Código
Benchmark.bm do |bm|
bm.report("count") do
10.times{ User.where('id>0').count<0 }
end
bm.report("empty?") do
10.times{ User.where('id>0').empty? }
end
end
aqui a essência
Console
E minha surpresa foi que ambas as consultas usam COUNT (*)
contagem
[DEBUG] (0.6ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.6ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.5ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.5ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id > 0)
vazio?
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.5ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.3ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.3ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.3ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.3ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
[DEBUG] (0.4ms) SELECT COUNT(*) FROM "users" WHERE "users"."deleted_at" IS NULL AND (id>0)
Resultado de referência
user system total real
count 0.020000 0.000000 0.020000 ( 0.023109)
empty? 0.020000 0.000000 0.020000 ( 0.018685)
Conclusão
Vazio? método é um pouco mais rápido do que contar, então não tenha medo de usá-lo, especialmente se você chamá-lo várias vezes. Minha preocupação e espero que alguém possa responder porque é mais rápido e porque está ligando para COUNT (*) porque me parece que o ActiveRecord está fazendo toda a mágica.
Atualização 29/07/2013
A fim de confirmar minhas dúvidas, eu executo os mesmos testes no banco de dados do stage e com uma grande amostra por 1000 vezes. E além das pequenas diferenças vazias? ainda batendo contar em tempo real.
user system total real
count 2.370000 0.100000 2.470000 ( 24.989100)
empty? 2.470000 0.090000 2.560000 ( 24.867949)