如果我切换到原始SQL查询,我的Rails查询开始变得复杂?你是做什么?

时间:2009-06-17 18:20:33

标签: sql ruby-on-rails activerecord

My Rails应用程序开始需要复杂的查询。我应该开始使用原始SQL查询吗? Rails社区的趋势是什么?

更新:

我现在没有书面查询,我想在开始之前先问这个问题。但这是我想要做的一个例子:

我的书有类别。我想说 -

Give me all books that were: 
-created_at (added to store) between date1 and date2
-updated_at before date3
-joined with books that exist in shopping carts right now

我还没有编写查询,但我认为rails版本将是这样的:

books_to_consider = Book.find(:all, 
                       :conditions => "created_at <= '#{date2}' AND created_at >= '#{date1}' AND updated_at <= '#{date3}'",
                       :joins => "as b inner join carts as c on c.book_id = b.id")

我并不是说ActiveRecord无法处理这个查询,但是为了便于阅读可以接受原始SQL(或者还有其他我不知道的限制)?

2 个答案:

答案 0 :(得分:13)

一般的想法是尽可能坚持ActiveRecord - 生成的查询,并仅在必要时使用SQL片段 。显式支持SQL片段,因为ActiveRecord的创建者意识到SQL无法完全抽象出来。

使用不带SQL片段的find方法通常会获得更好的可维护性。举个例子,试试:

Book.find(:all,
  :conditions => ["created_at >= ? AND created_at <= ? AND updated_at <= ?", 
                  date1, date2, date3]
  :include => :carts)

如果您将:inlude => :carts添加到has_many :carts模型,则Book会进行加入。正如您所看到的,不必涉及太多SQL。即使输入的引用和转义也可以留给Rails,同时仍然使用SQL文字来处理>=<=运算符。

更进一步,你可以让它更清晰:

class Book < AciveRecord::Base
  # Somewhere in your Book model:
  named_scope :created_between, lambda { |start_date, end_date|
    { :conditions => { :created_at => start_date..end_date } }
  }
  named_scope :updated_before, lambda { |date|
    { :conditions => ["updated_at <= ?", date] }
  }
  # ...
end

Book.created_between(date1, date2).updated_before(date3).find(:all,
  :include => :carts)

更新: named_scope s的重点当然是重用条件。您可以自行决定是否将一组条件置于命名范围内是否有意义。

答案 1 :(得分:3)

就像molf所说:include,.find()具有急切加载孩子的优势。 此外,有几个插件,如分页,将包装查找功能。你必须使用.find()来使用插件。

如果你有一个非常复杂的SQL查询,请记住.find()使用你的确切参数字符串。你总是可以注入自己的sql代码:

  

:conditions =&gt; [“id in union(select * from table ...

不要忘记.find()

有很多可选参数
  • :conditions - 一个SQL片段,例如“administrator = 1”,[“user_name =?”,username]或[“user_name =:user_name”,{:user_name =&gt;用户名 }]。参见介绍中的条件。
  • :order - 像“created_at DESC,name”这样的SQL片段。
  • :group - 应将结果分组的属性名称。使用GROUP BY SQL子句。
  • :having - 与+:group +结合使用此功能可用于过滤GROUP BY返回的记录。使用HAVING SQL子句。
  • :limit - 一个整数,用于确定应返回的行数限制。
  • :offset - 一个整数,用于确定应从中获取行的偏移量。所以在5,它会跳过0到4行。
  • :joins - 用于其他连接的SQL片段,例如“LEFT JOIN comments ON comments.post_id = id”(很少需要),用于:include选项的相同形式的命名关联,它将执行INNER JOIN关联的表,或包含字符串和命名关联的混合的数组。如果值是字符串,则记录将以只读方式返回,因为它们将具有与表的列不对应的属性。通过:readonly =&gt; false来覆盖。
  • :include - 指定应加载的关联。命名的符号引用已定义的关联。请参阅协会下的预先加载。
  • :select - 默认情况下,这是“SELECT * FROM”中的“*”,但是如果您想要进行连接但不包括已连接的列,则可以更改。使用SELECT SQL片段(例如“id,name”)获取字符串。
  • :from - 默认情况下,这是类的表名,但可以更改为备用表名(甚至是数据库视图的名称)。
  • :readonly - 将返回的记录标记为只读,以便无法保存或更新。
  • :lock - 像“FOR UPDATE”或“LOCK IN SHARE MODE”这样的SQL片段。 :lock =&gt; true给出连接的默认独占锁,通常为“FOR UPDATE”。

src:http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002553