find 方法
User.find(id) => 直接主键(id)查询
User.find(id,id2) => 查询多个
take 方法
User.take(number) => 返回 number 条数据
User.take => 返回一条数据 并无视排序
User.take(2) => 返回2条数据
first 方法
Client.first(number) => 返回前 number 条数据
User.first => 返回第一条数据
PS:first! 方法的行为和 first 方法类似,区别在于如果没有找到匹配的记录,first! 方法会抛出 ActiveRecord::RecordNotFound 异常。
last 方法
User.last(number) => 返回后 number 条数据
User.last => 返回最后一条数据
find_by 方法
User.find_by nickname: 'cwhh' => 找到 nickname 是 cwhh 的数据
PS:find_by! 方法的行为和 find_by 方法类似,区别在于如果没有找到匹配的记录,find_by! 方法会抛出 ActiveRecord::RecordNotFound 异常。
find_each 方法
User.find_each do |user|
p user
end
=> User 里面所有 user 数据
- :batch_size
User.find_each(batch_size: 5000) do |user|
p user
end
=> 检索 5000 条 打印 5000个 user 数据
- :start
User.find_each(start: 主键) do |user|
p user
end
=> 打印出从主键开始 到最后的 user 数据
- :finish
User.find_each(start: 开始的主键, finish: 结束的主键) do |user|
p user
end
=> 打印从 start 开始,finish结束的所有 user 数据
find_in_batches方法
User.find_in_batches do |user|
p user
end
=> 打印出 User 的所有user 的一个组合
PS: 和 find_each 类似,但是它是一次过将数据的组合传入块
条件查询
# 最简单一个例子
User.where("nickname = 'cwhh'")
=> 会查处所有 nickname为 cwhh 的用户
PS:上面的例子及其不安全,很容易受到 SQL 注入攻击的风险,要像下面这样写。
# 推荐写法
User.where("nickname = ?",'cwhh')
=> 会查出所有 nickname为 cwhh 的用户
# 多条件查询
User.where("nickname= ? AND status = ?", params[:name], false)
=> 会查处所有 nickname 为传入 参数对应 并且 状态 为 false 的所有用户
# 如果条件中有很多变量,那么上面这种写法的可读性更高。
Client.where("created_at >= :start_date AND created_at <= :end_date", {start_date: params[:start_date], end_date: params[:end_date]})
=> 查询 创建时间 大于等于传入开始时间,并且小于传入结束时间的 所有数据。
# 如果查询的是个数组
Course.where("'#{params[:id]}' = ANY (teachers)")
=> 查出 传入老师id对应的所有课程。
- 散列条件
User.where(nickname: 'cwhh') => 查出所有 nickname 是 cwhh 的数据
- 范围条件
User.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
=> 查出 创建时间 是 现在时间减去一天,到现在傍晚的 所有数据
- where.not
Client.where.not(nickname: 'cwhh') => 查询所有 nickname 不是 cwhh 的所有数据
排序
- order 方法
User.order(:created_at)
# 或
User.order("created_at")
=> 按创建时间排序 输出所有 User 数据
- ASC(升序) 或 DESC(降序)
User.order(created_at: :desc)
# 或
User.order(created_at: :asc)
# 或
User.order("created_at DESC")
# 或
User.order("created_at ASC")
=> 输出创建时间 升序 或者 降序的所有数据。
- 按多个字段排序
User.order(orders_count: :asc, created_at: :desc)
=> 输出订单数量降序,创建时间升序的所有数据
选择特定字段
Client.select("viewable_by, locked")
=> 输出 client 所有数据的 viewable_by 字段 和 locked 字段。
# 输出数据无重复
Client.select(:name).distinct
=> 输出 Client 所有数据里的 name 字段,并且不带重复
- 输出只有某字段的数据
User.select{ |user| user.realname }
=> 输出 realname 有值的数据
User.select{ |user| user.realname == nil }
=> 输出 所有 realname 没有值的数据
限量和偏移量
User.limit(5)
=> 打印5条user数据
User.limit(5).offset(30)
=> 打印5条user数据 从第31条开始
条件覆盖
User.where.not('nickname = ?','nil').limit(2).unscope(:limit)
=> 找到nickname不是空的user数据,unscope(:limit) 把limit限制给删除了
User.where.not('nickname=? AND realname=?','cwhh','123123').unscope(where: :realname)
=> 还可以把where的某条件删除
空关系
User.none
=> 返回一个空 Relation 对象,而且不执行查询
联结表
class Category < ApplicationRecord
has_many :articles
end
=> 一个创造者拥有多篇文章
class Article < ApplicationRecord
belongs_to :category
has_many :comments
has_many :tags
end
=> 一篇文章 只有一个创造者
=> 一篇文章 拥有多个评论
=> 一篇文章 拥有多个标签
class Comment < ApplicationRecord
belongs_to :article
has_one :guest
end
=> 一个评论 只有一对应一篇文章
=> 并且一个评论只对应一个客人
class Guest < ApplicationRecord
belongs_to :comment
end
=> 一个客人对应一个评论
class Tag < ApplicationRecord
belongs_to :article
end
=> 一个标签对应一篇文章
- 单个关联的联结
Category.joins(:articles)
=> 拥有文章的作者都打印出来
=> 如果这个作者有多篇文章,那么这个作者将会出现多次。
# 如果不想重复,可以
Category.joins(:articles).distinct
- 多个关联的联结
Article.joins(:category, :comments)
=> 打印出属于某个作者至少有一条评论的Article数据打印出来
=> 注意,依然会重复。
- 单层嵌套关联的联结
Article.joins(comments: :guest)
=> 打印出,拥有客人评论的文章数据
- 多层嵌套关联的联结
Category.joins(articles: [{ comments: :guest }, :tags])
=> 把拥有评论,拥有标签的文章创建者打印出来
及早关联
- 1+n问题
clients = Client.limit(10)
clients.each do |client|
puts client.address.postcode
end
# 这里查询 存在查询过多的问题 每条 client 都是去找他对应的address 和 postcode
#一共要找 1+10次address+10次postcode
# 作出改动
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
=> 这里一早把拥有地址的client 都找出来 然后再去找postcode,所以只执行了2次查询
- 多个关联的数组
Article.includes(:category, :comments)
=> 上面的代码会加载所有文章、所有关联的分类和每篇文章的所有评论。
Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
=> 上面的代码会查找 ID 为 1 的分类,并及早加载所有关联的文章、这些文章关联的标签和评论,以及这些评论关联的访客。
- 为及早关联指定条件
User.includes(:user_grounp).where(:user_grounp, {name: :teacher})
=> 提早关联有只有teacher小组里的的user用户数据
- 要想像上面的代码那样使用 where 方法,必须在 where 方法中使用散列。如果想要在 where 方法中使用字符串 SQL 片段,就必须用 references 方法强制使用联结表:
User.includes(:user_groups).where('user_groups.name=?','teacher').references(:user_groups)
作用域
#写在model里的
class User < ApplicationRecord
scope :sex, -> { where(sex: '男') }
end
#这两种方法达到的效果相同
class User < ApplicationRecord
def self.sex
where(sex: '男')
end
end
- 在作用域中可以使用别的作用域
class User < ApplicationRecord
scope :sex, -> { where(sex: '男') }
scope :sex_and_age, -> { sex.where("age > 18") }
end
- 调用
user = User.first
user.sex
- 传入参数
class User < ApplicationRecord
scope :created_before, ->(time) { where("created_at < ?", time) }
end
# 调用都是一样的
user = User.first
user.created_before(Time.now)
#当作用域需要接受参数时,推荐改用类方法。使用类方法时,这些方法仍然可以在关联对象上访问:
class Article < ApplicationRecord
def self.created_before(time)
where("created_at < ?", time)
end
end
#调用一样的
user.user_groups.created_before(time)
- 使用条件
class Article < ApplicationRecord
scope :created_before, ->(time) { where("created_at < ?", time) if time.present? }
end
class Article < ApplicationRecord
def self.created_before(time)
where("created_at < ?", time) if time.present?
end
end
- 应用默认作用域
class Client < ApplicationRecord
default_scope { where("removed_at IS NULL") }
end
class Client < ApplicationRecord
def self.default_scope
# 应该返回一个 ActiveRecord::Relation 对象
end
end
默认作用域在创建记录时同样起作用,但在更新记录时不起作用。例如:
合并作用域
class User < ApplicationRecord
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
User.active.inactive
# 混用where 方法
User.active.where(state: 'finished')
class User < ApplicationRecord
default_scope { where state: 'pending' }
scope :active, -> { where state: 'active' }
scope :inactive, -> { where state: 'inactive' }
end
User.all
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending'
User.active
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active'
User.where(state: 'inactive')
# SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive'
有一点需要特别注意,default_scope 总是在所有 scope 和 where 之前起作用。
在上面的代码中我们可以看到,在 scope 条件和 where 条件中都合并了 default_scope 条件。
enum 宏
enum status: {activation: 1, not_activation: 2}
User.create(name:'cwh',age:'18',status: 'activation')
User.find_by_name('cwh')
=> 这时候存到数据库时候 status 就是 1
=> 输出的时候 status 就是 activation
find_or_create_by 方法
这个方法 如果找到 就返回这个数据,没有找到就 创建一条这样的数据
find_or_initialize_by 方法
此方法和find_or_create_by差不错,只不过不是创建时候是new,不是create,要手动保存
使用 SQL 语句进行查找
Client.find_by_sql("SELECT * FROM clients
INNER JOIN orders ON clients.id = orders.client_id
ORDER BY clients.created_at desc")
pluck方法
User.pluck(:nickname)
=> 返回的是所有名字的一个组合的一个数组
检查对象是否存在
User.exists? => true
User.exists?(id: [1,2,3]) => 只要有一条对应记录存在就会返回 true
User.exists?(nickname: 'cwhh') => true
User.exists?(nickname: '1231231231') => false
计算
User.count => 200
网友评论