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

bun*_*ter 6 activerecord ruby-on-rails ruby-on-rails-3

莫因,

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

SELECT DISTINCT col1, col2 FROM table
Run Code Online (Sandbox Code Playgroud)

想象一下一个rails应用程序,它将餐饮组织为一个模型,每个Meal has_many :noodles Each Noodle都有属性(因此DB表列)colorshape.我的目标是让所有当前组合的数量colorshape单个餐.

由于AR不提供我使用的"不同"方法

my_meal.noodles.select("distinct color, shape")
Run Code Online (Sandbox Code Playgroud)

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

>> my_meal.noodles.select("distinct color, shape").count
=> 1606
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

bun*_*ter 12

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

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

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

my_meal.noodles.select("distinct color").count
Run Code Online (Sandbox Code Playgroud)

ActiveRecord创建以下SQL语句:

SELECT COUNT(distinct color) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295)
Run Code Online (Sandbox Code Playgroud)

如果一个选择两个或更多列并适用count

my_meal.noodles.select("distinct color, shape").count
Run Code Online (Sandbox Code Playgroud)

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

SELECT COUNT(*) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295)
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

除此之外,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
Run Code Online (Sandbox Code Playgroud)

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

my_meal.noodles.group("shape").count
Run Code Online (Sandbox Code Playgroud)

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

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

my_meal.noodles.select("distinct color, shape").group("color, shape").all.count
Run Code Online (Sandbox Code Playgroud)

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


蒂姆,谢谢你的帮助