为什么这两个查询以不同的顺序检索记录?
Why do these two queries retrieve records in different orders?
我有一个 Profile
可以是 published
。 A profile
belongs_to :user
和 has_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
是某种排序标准。
我有一个 Profile
可以是 published
。 A profile
belongs_to :user
和 has_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
是某种排序标准。