Rails - 按连接表数据排序

Posted

技术标签:

【中文标题】Rails - 按连接表数据排序【英文标题】:Rails - Sort by join table data 【发布时间】:2012-03-01 03:26:58 【问题描述】:

我有一个 RoR 项目正在进行中。以下是我的模型的适用部分。

首页

has_many :communities, :through => :availabilities
has_many :availabilities, :order => "price ASC"

社区

has_many :homes, :through => :availabilities
has_many :availabilities

可用性

belongs_to :home
belongs_to :community

数据库中的“availabilities”表有额外的数据列“price”

所以现在我可以打电话了

@home.availabilities.each do |a|
  a.community.name
  a.price

并根据需要取回按价格排序的可用性数据。我的问题是这样的:

有没有办法通过avaliabilities.first.price(第一=最低)自动订购房屋?也许是default_scope :order

【问题讨论】:

【参考方案1】:

在this related post 的帮助下解决了这个问题。

我将排序从 Home 模型移到了 Availability 模型中:

可用性

default_scope :order => "price ASC"

然后我急切地将可用性加载到 Home 模型中并按价格排序:

首页

default_scope :include => :availabilities, :order => "availabilities.price ASC"

【讨论】:

【参考方案2】:

我建议避免使用default_scope,尤其是在另一张桌子上的价格之类的东西上。每次您使用该表时,都会进行连接和排序,这可能会在复杂查询中产生奇怪的结果,并且无论如何都会使您的查询变慢。

它自己的范围没有什么问题,它更简单,更清晰,你可以让它变得简单:

scope :ordered, ->  includes(:availabilities).order('availabilities.price') 

PS:记得在price上添加索引;另请参阅此处的其他出色答案,以在 join/include 之间做出决定。

【讨论】:

你需要使用includes而不是joins吗? 视情况而定,@TeWu 在解释他的答案的差异方面做得很好。尤其是 Tom Dallimore 博客的链接非常棒。 @ecoologic ***.com/questions/56669484/…【参考方案3】:

@ecoologicanswer:

scope :ordered, ->  includes(:availabilities).order('availabilities.price') 

很好,但应该提到includes 可以,并且在某些情况下应该由joins 代替。它们都有自己的最佳用例(请参阅:#1、#2)。

从实际的角度来看,有两个主要区别:

    includes 加载相关记录;在这种情况下 Availability 记录。 joins 不加载任何关联的记录。因此,当您想使用连接模型中的数据时,您应该使用includes,例如在某处显示price。另一方面,如果您打算仅在查询中使用连接模型的数据,则应使用joins,例如在ORDER BYWHERE 子句中。

    includes 加载所有记录,而joins 仅加载具有关联连接模型的那些记录。因此,在 OP 的情况下,Home.includes(:availabilities) 将加载所有房屋,而Home.joins(:availabilities) 将仅加载与至少一个可用性相关联的房屋。

另见this question。

【讨论】:

最佳用例链接现已断开 感谢您的信息。链接已断开,因为链接的博客已死……真可惜。我已经链接到这篇博文的存档版本,还添加了一个链接到一些关于这个主题的非史前博文;)【参考方案4】:

在 Rails 5.2+ 中,将字符串参数传递给 order 方法时,您可能会收到弃用警告:

弃用警告:使用非属性参数调用的危险查询方法(其参数用作原始 SQL 的方法):“table.column”。 Rails 6.0 将不允许使用非属性参数。不应使用用户提供的值(例如请求参数或模型属性)调用此方法。

要解决这个问题,你可以使用Arel.sql()

scope :ordered, -> 
  includes(:availabilities).order(Arel.sql('availabilities.price'))

【讨论】:

【参考方案5】:

实现此目的的另一种方法:

scope :ordered, ->  includes(:availabilities).order(Availability.arel_table[:price]) 

你也可以指定ASC方向

scope :ordered, ->  includes(:availabilities).order(Availability.arel_table[:price].asc) 

DESC:

scope :ordered, ->  includes(:availabilities).order(Availability.arel_table[:price].desc) 

ActiveRecord 模型上使用arel_table 可以让您在表名更改时避免出现这种情况(但这种情况很少发生)。

请注意,最好添加main_table#id 以进行确定排序。

所以最终版本是:

scope :ordered, -> 
  includes(:availabilities).
    order(Availability.arel_table[:price].asc, order(Home.arel_table[:id].asc)

【讨论】:

【参考方案6】:

您还可以像这样对链接表进行排序(例如):

class User
  has_many :posts
end

class Post
  belongs_to :user

  scope :sorted_by_user_and_title, ->  
    joins(:user).merge(
     User.order(first_name: :asc, last_name: :asc)
    )
    .order(title: :desc)
    # SELECT * FROM `posts`
    # INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
    # ORDER BY
    # `users`.`first_name` ASC, `users`.`last_name` ASC, `posts`.`title` DESC;
  
  scope :sorted_by_title_and_user, ->  
    order(title: :desc)
    .joins(:user).merge(
     User.order(first_name: :asc, last_name: :asc)
    )
    # SELECT * FROM `posts`
    # INNER JOIN `users` ON `posts`.`user_id` = `users`.`id`
    # ORDER BY
    # `posts`.`title` DESC, `users`.`first_name` ASC, `users`.`last_name` ASC;
  
end

问候

【讨论】:

以上是关于Rails - 按连接表数据排序的主要内容,如果未能解决你的问题,请参考以下文章

Rails 4:如何从数据库中删除或删除(连接)表?

Mysql查询连接两个表按一个表列排序

Rails 方法用于两个相关连接表之间的关系并同时强制数据完整性

从数据表中的另一个表添加可排序列

在SQL语句中,如何把两张表的数据按时间排序查询?

核心数据 - NSFetchedResultController 按其他表字段排序数据