Rails是范围变量的最佳方法

时间:2016-12-10 00:35:40

标签: ruby-on-rails performance scope

我有一个'课程'具有以下属性的模型;

Course
  Price - float 
  Featured - boolean

我的问题如下,我需要控制器中的4个列表,最近的课程,付费课程,免费课程和特色课程。

按照以下方式编写控制器是个好习惯吗?

def index
  @courses = Course.order(created_at: :desc)

  @free_courses = []
  @courses.map {|c| @free_courses << c if c.price == 0}

  @premium_courses = []
  @courses.map {|c| @premium_courses << c if c.price> 0}

  @featured_courses = []
  @courses.map {|c| @featured_courses << c if c.featured}
end

或单独进行咨询?

def index
  @courses = Course.order(created_at: :desc)
  @free_courses = Course.where("price == 0")
  @premium_courses = Course.where("price > 0")
  @featured_courses = Course.where(featured: true)
end

我查看了日志,第一个选项更具性能,但我怀疑它是否是反对伙伴。

谢谢大家!

3 个答案:

答案 0 :(得分:2)

随着Course表的大小增加,第二种方法将比第一种方法更快。第一种方法必须迭代表中的每条记录4次。第二种方法只创建一个与where子句匹配的记录的关系,因此它的工作量较少。

此外,第二种方法具有懒惰的优点。每个查询仅在使用时运行,因此可以沿代码路径进一步更改。它更灵活。

请注意,对于在处理逻辑的Course模型上创建范围的第二种方法,这将是一种改进。例如,课程,免费课程,高级课程和特色课程各一个。这样做的好处是可以将数据库逻辑放在模型中而不是控制器中,从而可以更容易地重用和维护数据库逻辑。

答案 1 :(得分:1)

第二种方法更好,因为当您使用.where()方法时,您将在数据库本身而不是控制器中安排查询。

答案 2 :(得分:0)

通常不好的做法是在Rails(即Course.mapCourse.all)中迭代数据库中的所有记录,以提高性能和内存使用率。随着数据库的增长,这会成为指数级问题。使用Course.where()方法要好得多。您可能需要默认的排序顺序,因此您可以在模型中添加一行。

default_scope { order(created_at: :desc) }

然后你可以在控制器中执行此操作,默认情况下他们会进行排序:

@courses = Course.all

我还建议在您的模型中添加范围,以便于访问。 所以在你的course.rb文件中

scope :free -> { where("price == 0") }
scope :premium -> { where("price > 0") }
scope :featured -> { where(featured: true) }

然后在您的控制器中,您可以这样做:

@courses = Course.all
@free_courses = Course.free
@premium_courses = Course.premium
@featured_courses = Course.featured

如果您需要将这些范围组合起来,也可以链接这些范围,以便您可以执行以下操作:

@mixed_courses = Course.premium.featured

正如其他人所解释的那样,Model.where()通过在where("Write Pure SQL QUERIES HERE")内传递sql来执行数据选择,其中常规ruby可枚举方法(.map)迭代数组,必须将其实例化为ruby对象。这就是内存/性能问题受到重创的地方。如果您正在使用小型数据集,那就没关系了,但任何有数据量的内容都会变得很难看。