为什么这两个查询以不同的顺序检索记录?

Why do these two queries retrieve records in different orders?

我有一个 Profile 可以是 published。 A profile belongs_to :userhas_many :ratings.

一个User has_one :profile,和has_many :ratings

一个Rating belongs_to :profile && belongs_to :user.

这些是上述模型的架构:

Profile.rb:

# == Schema Information
#
# Table name: profiles
#
#  id                      :integer          not null, primary key
#  first_name              :string
#  last_name               :string
#  created_at              :datetime         not null
#  updated_at              :datetime         not null
#  user_id                 :integer

User.rb:

# == Schema Information
#
# Table name: users
#
#  id                     :integer          not null, primary key
#  email                  :string           default(""), not null
#  created_at             :datetime         not null
#  updated_at             :datetime         not null
#  first_name             :string
#  last_name              :string

Rating.rb

# == Schema Information
#
# Table name: ratings
#
#  id         :integer          not null, primary key
#  speed      :integer          default(0)
#  passing    :integer          default(0)
#  tackling   :integer          default(0)
#  dribbling  :integer          default(0)
#  profile_id :integer
#  user_id    :integer
#  created_at :datetime         not null
#  updated_at :datetime         not null
#

注意 coach = User.find(7).

当我执行此查询时:

>p = Profile.published.where(id: coach.ratings.order(passing: :desc).pluck(:profile_id))
  (0.4ms)  SELECT "ratings"."profile_id" FROM "ratings" WHERE "ratings"."user_id" =  ORDER BY "ratings"."passing" DESC  [["user_id", 7]]
  Profile Load (1.1ms)  SELECT "profiles".* FROM "profiles" WHERE "profiles"."status" =  AND "profiles"."id" IN (52, 14, 24, 29)  [["status", 1]]
> p.ids
=> [24, 14, 52]

注意 p.ids 生成的 profile.ids 的顺序。

然而,当我只是 运行 内部查询时,我得到了不同的顺序:

> coach.ratings.order(passing: :desc).limit(3).pluck(:profile_id)
   (0.8ms)  SELECT  "ratings"."profile_id" FROM "ratings" WHERE "ratings"."user_id" =  ORDER BY "ratings"."passing" DESC LIMIT   [["user_id", 7], ["LIMIT", 3]]
=> [52, 14, 24]

是什么导致了差异?为什么我不能让第一个查询始终产生与后一个查询相同的结果?

编辑 1

请注意,即使我在第一个查询中对 ID 的顺序进行硬编码,它仍会按原始顺序返回结果:

[19] pry(main)> cids = coach.ratings.order(passing: :desc).limit(3).pluck(:profile_id)
   (0.7ms)  SELECT  "ratings"."profile_id" FROM "ratings" WHERE "ratings"."user_id" =  ORDER BY "ratings"."passing" DESC LIMIT   [["user_id", 7], ["LIMIT", 3]]
=> [52, 14, 24]
[21] pry(main)> q = Profile.published.where(id: cids)
  Profile Load (0.7ms)  SELECT "profiles".* FROM "profiles" WHERE "profiles"."status" =  AND "profiles"."id" IN (52, 14, 24)  [["status", 1]]
[22] pry(main)> q.ids
=> [24, 14, 52]

编辑 2

当我尝试以下 joins 查询时,它 returns profiles 违反了 published 状态(也就是 returns 配置文件 status: :unpublished 当它不应该时):

> a = Profile.joins(:ratings).where(status: :published, id: coach.ratings.pluck(:profile_id)).order('ratings.passing DESC')
   (0.4ms)  SELECT "ratings"."profile_id" FROM "ratings" WHERE "ratings"."user_id" =   [["user_id", 7]]
  Profile Load (1.8ms)  SELECT "profiles".* FROM "profiles" INNER JOIN "ratings" ON "ratings"."profile_id" = "profiles"."id" WHERE "profiles"."status" =  AND "profiles"."id" IN (24, 52, 29, 14) ORDER BY ratings.passing DESC  [["status", 1]]

> o = Profile.find(29)
  Profile Load (0.8ms)  SELECT  "profiles".* FROM "profiles" WHERE "profiles"."id" =  LIMIT   [["id", 29], ["LIMIT", 1]]
[59] pry(main)> o.status
=> "unpublished"
> a.ids
=> [52, 14, 24, 14, 24]

编辑 3

来自上述查询的服务器错误:

PG::InvalidColumnReference: ERROR:  for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ... AND "profiles"."id" IN (24, 52, 29, 14) ORDER BY ratings.pa...
                                                             ^
: SELECT DISTINCT "profiles".* FROM "profiles" INNER JOIN "ratings" ON "ratings"."profile_id" = "profiles"."id" WHERE "profiles"."status" =  AND "profiles"."id" IN (24, 52, 29, 14) ORDER BY ratings.passing DESC

编辑 3a

当我尝试从错误页面的 REPL 访问 @profiles 时,我得到的是:

>> @profiles
!! #<ActiveRecord::StatementInvalid: PG::InvalidColumnReference: ERROR:  for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ... AND "profiles"."id" IN (24, 52, 29, 14) ORDER BY ratings.pa...
                                                             ^
: SELECT DISTINCT "profiles".* FROM "profiles" INNER JOIN "ratings" ON "ratings"."profile_id" = "profiles"."id" WHERE "profiles"."status" =  AND "profiles"."id" IN (24, 52, 29, 14) ORDER BY ratings.passing DESC>
>>

原因是where查询没有return按照输入的顺序记录。因此 where 内的 id 排序不会影响结果。如果您想对 p 中的记录进行排序,您应该在 where 查询之后链接 order。试试这个:

Profile.published.joins(:ratings).where(id: coach.ratings.pluck(:profile_id)).order('ratings.speed')

OP 编辑​​

所以原因是正确的,但修复是错误的。

我终于通过另一种方式提出这个问题找到了解决方法,。但为了完整起见,我在这里添加答案:

Profile.published
  .joins(:ratings)
  .where(ratings: { user_id: coach.id } )
  .order('ratings.passing')

第一个查询的顺序不能确定使用第一个结果作为选择器的第二个查询的顺序。选择并不意味着排序。

解释一下你的例子:

coach_rating_profile_ids = coach.ratings.pluck(:profile_id)
profiles = Profile.where(id: coach_rating_profile_ids)

coach_rating_profile_ids中的id可以任意顺序,不会影响选中的Profile对象的顺序。您可以轻松地试验 coach_rating_profile_ids 的排序以查看。示例:

profiles = Profile.where(id: coach_rating_profile_ids.reverse)

将给出相同的结果(假设没有发生其他交易)。

如果您想在选择的配置文件集中使用特定顺序,则必须在其上使用明确的 .order()(而不是在 id 选择器上)。例如:

profiles = Profile.where(id: coach_rating_profile_ids).order(xxxx)

其中 xxxx 是某种排序标准。