ActiveRecord限制方法似乎不尊重关系的顺序

时间:2017-03-08 19:07:24

标签: sql ruby-on-rails ruby activerecord

我想我可能已经发现ActiveRecord_AssociationRelation#limit的错误,但我不确定并想知道是否有更简单的解释。我正在运行Rails 5.0.2。

假设以下型号:

class Company < ApplicationRecord
    has_many :contacts
end

class Contact < ApplicationRecord
    belongs_to :company
    attr_accessor :engagement
end

我将首先使用此查询选择所有联系人:

contacts = Company.last.contacts.order(engagement: :desc)

这将输出以下SQL:

Company Load (1.4ms)  SELECT  "companies".* FROM "companies" ORDER BY "companies"."id" DESC LIMIT 1
Contact Load (36.9ms)  SELECT "contacts".* FROM "contacts" WHERE "contacts"."company_id" = 94 ORDER BY "contacts"."engagement" DESC

到目前为止一切顺利。现在假设我想从关系中的前20个联系人中获取一组id。我希望返回数组中的id的顺序与它们在关系中出现的顺序相匹配。

首先我将使用pluck(:id)[0..19]。这将返回以下内容:

contacts.pluck(:id)[0..19]
#=> [861, 862, 802, 868, 794, 1583, 1267, 1857, 1081, 1686, 1041, 1535, 1560, 1707, 1770, 1600, 1205, 2027, 1179, 1184]

接下来我尝试map(&:id)[0..19],毫不奇怪地返回相同的结果:

contacts.map(&:id)[0..19]
#=> [861, 862, 802, 868, 794, 1583, 1267, 1857, 1081, 1686, 1041, 1535, 1560, 1707, 1770, 1600, 1205, 2027, 1179, 1184]

最后我尝试limit(20).pluck(:id),它会返回意外的结果:

contacts.limit(20).pluck(:id)
(4.5ms)  SELECT  "contacts"."id" FROM "contacts" WHERE "contacts"."company_id" = 94 ORDER BY "contacts"."engagement" DESC LIMIT 20
#=> [861, 862, 802, 868, 794, 1686, 1081, 1857, 1267, 1600, 1583, 1357, 1560, 1041, 1535, 1201, 1707, 1770, 2075, 1937]

请注意,在前两个数组中,id的顺序相同,但是当我使用limit时,它们会出现乱序,即使生成的SQL显然包含ORDER BY子句。

值得注意的是,对于engagement > 0.0的前几个联系人,ID的顺序始终是正确的。一旦engagement的值变为0.0,订单就会开始变化。例如,以下是这20个联系人的参与号码:

[56.0, 44.0, 3.0, 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

有人能解释一下这里发生了什么吗?

2 个答案:

答案 0 :(得分:1)

正如我们在评论中发现的那样,问题是当order遇到两个具有相同engagement值的对象时,它会排序&#34;排序&#34;它以某种特定的方式。

将附加参数传递给ORDER子句(例如id)会有什么帮助:

Company.last.contacts.order(engagement: :desc, id: :asc)

答案 1 :(得分:0)

这可能只是数据库选择返回结果的顺序。在SQL中,除非您指定order by,否则无法保证订购。即使在指定的情况下,我相信在相同排序结果的块中(即在这种情况下为0.0),也无法保证结果的排序 - 它可以是数据库认为最合适的任何顺序, /或对该特定查询有效。

如果您需要按特定顺序返回结果,那么最好将其添加为额外的order by参数(在这种情况下为id

编辑:以下是关于该主题的更多讨论:How does order by clause works if two values are equal?

SELECT "contacts"."id"SELECT "contacts".*使用不同的索引,这很有可能导致排序不同。