Rails中的查询(基础篇)

作者:周星 发布:2014-06-16

        总结了一下 Rails 中的查询,本系列分享包含 ActiveRecord 的各种查询方法,主要内容有:

  1. 使用不同的方法和条件查询
  2. 通过指定的排序、查询条件、分组查询数据
  3. 1 + N 问题(会在下一节详细介绍)
  4. 使用动态的 finder
  5. 数据的存在性检验
  6. 数学公式(sum、average、sum、max、min等)

注:本分享适用于 Rails3.2.X 版本,有些内容在其它版本可能不适用

       通常我们使用原生的 SQL 来做数据库查询,但是Rails 的 ActiveRecord 模块提供了更简便、直观的方式来生成 SQL,ActiveRecord 模块提供了多种方法,每一种方法允许你通过传入条件参数来进行数据库查询,这些方法如下:

  1. where
  2. select
  3. group
  4. order
  5. reorder
  6. reverse_order
  7. limit
  8. offset
  9. joins
  10. includes
  11. lock
  12. readonly
  13. from
  14. having

所有的上述方法都会返回一个 ActiveRelation 对象,ActiveRecord 查询操作可以总结为:

  1. 将 ruby 代码转化为 SQL语句
  2. 执行 SQL,到数据库中查询出数据
  3. 把结果数据的每一行实例化为一个等价的 ruby 对象(这样我们可以对其使用 ruby 方法)
  4. 执行 after_find 回调(可能没有)

       我们跳过 find / first / first! / last / last! 方法,这里要注意两个问题:1.加感叹号的方法会在查询不到结果时抛出 RecordNotFound 异常;2\. find 方法可以传入一个主键的数组,当数组中存在任意主键查询无结果都会抛出 RecordNotFound异常。

1. 批量获取数据

通过 find_each 和 find_in_batch,具体使用场景及使用方法见 find_each 批量获取数据

2. 查询条件

where 方法允许你指定查询条件来限制返回的数据,查询条件可以为字符串、数据和哈希。

2.1 字符串

字符串作为 where 查询参数可以这样使用

Boy.where("age = 24")

这个查询会找到年纪为 24 的男生。

# 注意:使用字符串作为查询条件时,可能会引发 SQL 注入事件,比如:
Boy.where("first_name LIKE '%#{params[:first_name]}%'") # 就是不安全的,我们将在 Rails 安全一节来详细解释

2.2 数组

怎么传入多个参数?来看这个例子:

Boy.where("first_name = ? AND age = ?", params[:first_name], 24)

       如果你写过 C 语言的 printf,那这段就很好理解了,两个问号作为占位符,分别指代 params[:first_name] 和 24,这些工作都是 ActiveRecord 完成的,在实战中,我们应该更多的使用这种问号占位的方式,而不是使用字符串。

2.3 placeholder

类似于问号参数占位风格,你也可以通过 key/value 的方式来制定条件

Boy.where("created_at >= :start_date AND created_at <= :end_date",
  {:start_date => params[:start_date], :end_date => params[:end_date]})

这种看起来要更清晰一些,当你有大量的条件参数

2.4 范围查询

如果你想在数据库中查询一段范围,例如在某段时间内注册的用户,你可以这样:

User.where(:created_at => (params[:start_date].to_date)..(params[:end_date].to_date))

他会生成如下的 SQL:

SELECT "users".* FROM "users" WHERE ("users"."created_at" BETWEEN '2010-09-29' AND '2010-11-30')

2.5 哈希

可以使用 hash 作为条件做查询,这种方式的好处是可读性比较强

Boy.where(first_name: 'zhou')

hash 里面的值可以是数组,作为 in 查询的值

Boy.where(age: [22, 23])

等价的 sql 为:

SELECT * FROM boys WHERE (boys.age IN (1,3,5))

3. 排序

排序一般使用 order 方法

# 按照创建时间排序
Boy.where(age: 22).order("created_at ASC")
# 按照创建时间排序,如果创建时间相同,则按照 id 排序
Boy.where(age: 22).order("created_at ASC, id ASC")

4. select

find 方法和 where 方法默认查询所有的字段,通过向select 方法传递参数,你可以只查出指定字段的值。

Boy.select("age, first_name, created_at").order("created_at DESC")

但是需要注意:select 方法只会通过你 select 出来的字段初始化对象,所以当你想通过对象得到其它的属性时,例如:

boy = Boy.select("age, first_name, created_at").order("created_at DESC").first
puts boy.last_name

会抛出异常:

ActiveModel::MissingAttributeError: missing attribute: <attribute>

       因为你并未 select last_name 这个属性,还有一个需要注意的问题是:id 方法不会抛出这个异常,但是有很多关联操作都需要 id 这个属性,所以最好在 select 的时候加入 id 。

如果你想通过 select 作唯一性查询,可以使用 uniq,去掉唯一性查询则使用 uniq(false):

b = Boy.select("age").uniq
# 去掉唯一性查询
b.uniq(false)

5. limit 和 offset

limit 和 offset 用法意义同 sql ,一般在分页中较常见,在此不多做解释,看一个例子:

# 取第4、5个boy
b = Boy.where(age: 22).offset(3).limit(2)

6. group

group 方法可以轻松实现 sql 中的 group by 子句:

Boy.select("date(created_at) as boy_date, sum(age) as total_age").group("date(created_at)"

7. having

在 SQL 中增加 HAVING 子句原因是,WHERE 关键字无法与合计函数一起使用。SQL 中的 having, 在 Rails 中,你可以直接在查询出的 relation 对象后使用 .having 方法。

8. 条件覆盖

这里分别介绍几个方法:except、 only、 order 和 reverse_order

except 和 only 方法需要传递参数,参数为查询方法符号,多个方法用逗号分隔

# 去掉order条件
Boy.where('id > 10').limit(20).order('id ASC').except(:order)
# 只使用order 和 where 条件
Boy.where('id > 10').limit(20).order('id DESC').only(:order, :where)

reorder 方法会覆盖掉默认的 order 排序;reverse_order 会将当前排序倒过来,在这里不做示例。


Rails 中的查询(基础篇)到此结束,下一节我们将介绍 1 + N查询。

支付宝扫码赞助博主


评论(0)