Durante una discusión con un compañero de trabajo sobre la optimización de consultas sobre rieles, ¿necesito demostrar mi teoría de que el recuento es más rápido que el vacío? porque en el primer método, el registro activo se realiza directamente sobre sql y en el segundo está verificando la longitud de la matriz dada por el resultado.
Mi entorno
ruby 1.9.3-p448
rails 3.2.6
Entonces, para demostrarlo, hice el siguiente punto de referencia
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
aquí la esencia
Consola
Y mi sorpresa fue que ambas consultas usan COUNT (*)
contar
[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)
¿vacío?
[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 referencia
user system total real
count 0.020000 0.000000 0.020000 ( 0.023109)
empty? 0.020000 0.000000 0.020000 ( 0.018685)
Conclusión
¿Vacío? El método es un poco más rápido que contar, así que no temas usarlo, especialmente si lo llamas varias veces. Mi preocupación y espero que alguien pueda responder por qué es más rápido y por qué está llamando a COUNT (*) porque me parece que ActiveRecord está haciendo toda la magia.
Actualización 29/07/2013
Para confirmar mis dudas, ejecuto las mismas pruebas en la base de datos del escenario y con una muestra grande 1000 veces. ¿Y además de las leves diferencias vacías? sigue superando la cuenta en tiempo real.
user system total real
count 2.370000 0.100000 2.470000 ( 24.989100)
empty? 2.470000 0.090000 2.560000 ( 24.867949)