Rails 3:Relation.count和Relation.all.count之间的区别

莫因,

我偶然发现了ActiveRecord的不一致。
我试图在大表的两列中获取所有使用的值组合。 第一个想法:

SELECT DISTINCT col1, col2 FROM table 

想象一下一个rails应用程序,它将餐食组织成一个模型,每个餐点都有多种has_many :noodles每个面条都有属性(因此DB表格列)的colorshape 。 我的目标是获得一餐的所有现有colorshape组合的数量。

由于AR不提供我使用的“独特”方法

 my_meal.noodles.select("distinct color, shape") 

得到(在rails控制台stdout中)一个6行输出的8个Noodle对象(分别是它们的String表示)。 但:

 >> my_meal.noodles.select("distinct color, shape").count => 1606 

事实上, my_meal包含1606面条。 如果我将Relation转换为数组并获取它的大小或使用.all.count ,结果是正确的。

所以我的问题是,为什么AR输出8个对象但是计算所有数据库线?

这里似乎提到了类似的问题,但没有给出答案。

蒂姆,谢谢和最诚挚的问候

好的,感谢tadman让我朝着正确的方向前进。

我挖得更深(特别是在日志文件中),我发现的有点奇怪。

问题是由所选列的数量引起的。 如果只选择一列并计算结果

 my_meal.noodles.select("distinct color").count 

ActiveRecord创建以下SQL语句:

 SELECT COUNT(distinct color) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295) 

如果一个选择两个或多个列并对其应用count

 my_meal.noodles.select("distinct color, shape").count 

ActiveRecord忘记了该select子句并创建:

 SELECT COUNT(*) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295) 

这可能是正确的,因为(SQL的) COUNT只允许一列或更少的列作为参数。 在count之前添加一个group ,一切都很好:

 my_meal.noodles.select("distinct color, shape").group("color, shape").count SELECT COUNT(*) AS count_all, color, shape AS color_shape FROM "NOODLES" WHERE ("NOODLES".meal_id = 295) GROUP BY color, shape 

除了这个AS color_shape之外,它完全符合我的预期。 但是……只有它返回:

 >> my_meal.noodles.select("distinct color, shape").group("color, shape").count => {star=>309, circle=>111, spaghetti=>189, square=>194, triangle=>179, bowtie=>301, shell=>93, letter=>230} >> my_meal.noodles.select("distinct color, shape").group("color, shape").count.class => ActiveSupport::OrderedHash 

这个奇怪的返回值是(除了依赖于DB的顺序)与结果和返回值相同

 my_meal.noodles.group("shape").count 

结论:
正如这里指出的那样 ,关系(可能是数学或关系)和ActiveRecord :: Relations之间仍然存在差距。
我可以看到尽可能经常地将结果压入模型的优点(至少在Rails应用程序的上下文中)。
然而,实际关系不是几种操作相结合的结果,而是这些操作连接的结果。 一般来说,ActiveRecord :: Relations的可链接性是一件好事,但是有些设计决定是我无法遵循的。
如果你不能依赖于每个行动都返回一个新的合作关系的保证,那么它就会失去其大部分的诱惑力。

至于我的问题的解决方案,我将使用上述group解决方案和计数操作的某种脏方法:

 my_meal.noodles.select("distinct color, shape").group("color, shape").all.count 

这会将结果压缩到可接受的最小值,然后再将它们拉出数据库并创建昂贵的对象来计算它们。 或者,可以使用手写SQL查询,但为什么有Rails并且不使用它,是吧? 😉

谢谢你的帮助,
蒂姆