find 方法
User.find(id) => 直接主鍵(id)查詢
User.find(id,id2) => 查詢多個(gè)
take 方法
User.take(number) => 返回 number 條數(shù)據(jù)
User.take => 返回一條數(shù)據(jù) 并無視排序
User.take(2) => 返回2條數(shù)據(jù)
first 方法
Client.first(number) => 返回前 number 條數(shù)據(jù)
User.first => 返回第一條數(shù)據(jù)
PS:first! 方法的行為和 first 方法類似,區(qū)別在于如果沒有找到匹配的記錄,first! 方法會(huì)拋出 ActiveRecord::RecordNotFound 異常。
last 方法
User.last(number) => 返回后 number 條數(shù)據(jù)
User.last => 返回最后一條數(shù)據(jù)
find_by 方法
User.find_by nickname: 'cwhh' => 找到 nickname 是 cwhh 的數(shù)據(jù)
PS:find_by! 方法的行為和 find_by 方法類似,區(qū)別在于如果沒有找到匹配的記錄,find_by! 方法會(huì)拋出 ActiveRecord::RecordNotFound 異常。
find_each 方法
User.find_each do |user|
p user
end
=> User 里面所有 user 數(shù)據(jù)
- :batch_size
User.find_each(batch_size: 5000) do |user|
p user
end
=> 檢索 5000 條 打印 5000個(gè) user 數(shù)據(jù)
- :start
User.find_each(start: 主鍵) do |user|
p user
end
=> 打印出從主鍵開始 到最后的 user 數(shù)據(jù)
- :finish
User.find_each(start: 開始的主鍵, finish: 結(jié)束的主鍵) do |user|
p user
end
=> 打印從 start 開始,finish結(jié)束的所有 user 數(shù)據(jù)
find_in_batches方法
User.find_in_batches do |user|
p user
end
=> 打印出 User 的所有user 的一個(gè)組合
PS: 和 find_each 類似,但是它是一次過將數(shù)據(jù)的組合傳入塊
條件查詢
# 最簡(jiǎn)單一個(gè)例子
User.where("nickname = 'cwhh'")
=> 會(huì)查處所有 nickname為 cwhh 的用戶
PS:上面的例子及其不安全,很容易受到 SQL 注入攻擊的風(fēng)險(xiǎn),要像下面這樣寫。
# 推薦寫法
User.where("nickname = ?",'cwhh')
=> 會(huì)查出所有 nickname為 cwhh 的用戶
# 多條件查詢
User.where("nickname= ? AND status = ?", params[:name], false)
=> 會(huì)查處所有 nickname 為傳入 參數(shù)對(duì)應(yīng) 并且 狀態(tài) 為 false 的所有用戶
# 如果條件中有很多變量,那么上面這種寫法的可讀性更高。
Client.where("created_at >= :start_date AND created_at <= :end_date", {start_date: params[:start_date], end_date: params[:end_date]})
=> 查詢 創(chuàng)建時(shí)間 大于等于傳入開始時(shí)間,并且小于傳入結(jié)束時(shí)間的 所有數(shù)據(jù)。
# 如果查詢的是個(gè)數(shù)組
Course.where("'#{params[:id]}' = ANY (teachers)")
=> 查出 傳入老師id對(duì)應(yīng)的所有課程。
- 散列條件
User.where(nickname: 'cwhh') => 查出所有 nickname 是 cwhh 的數(shù)據(jù)
- 范圍條件
User.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
=> 查出 創(chuàng)建時(shí)間 是 現(xiàn)在時(shí)間減去一天,到現(xiàn)在傍晚的 所有數(shù)據(jù)
- where.not
Client.where.not(nickname: 'cwhh') => 查詢所有 nickname 不是 cwhh 的所有數(shù)據(jù)
排序
- order 方法
User.order(:created_at)
# 或
User.order("created_at")
=> 按創(chuàng)建時(shí)間排序 輸出所有 User 數(shù)據(jù)
- ASC(升序) 或 DESC(降序)
User.order(created_at: :desc)
# 或
User.order(created_at: :asc)
# 或
User.order("created_at DESC")
# 或
User.order("created_at ASC")
=> 輸出創(chuàng)建時(shí)間 升序 或者 降序的所有數(shù)據(jù)。
- 按多個(gè)字段排序
User.order(orders_count: :asc, created_at: :desc)
=> 輸出訂單數(shù)量降序,創(chuàng)建時(shí)間升序的所有數(shù)據(jù)
選擇特定字段
Client.select("viewable_by, locked")
=> 輸出 client 所有數(shù)據(jù)的 viewable_by 字段 和 locked 字段。
# 輸出數(shù)據(jù)無重復(fù)
Client.select(:name).distinct
=> 輸出 Client 所有數(shù)據(jù)里的 name 字段,并且不帶重復(fù)
- 輸出只有某字段的數(shù)據(jù)
User.select{ |user| user.realname }
=> 輸出 realname 有值的數(shù)據(jù)
User.select{ |user| user.realname == nil }
=> 輸出 所有 realname 沒有值的數(shù)據(jù)
限量和偏移量
User.limit(5)
=> 打印5條user數(shù)據(jù)
User.limit(5).offset(30)
=> 打印5條user數(shù)據(jù) 從第31條開始
條件覆蓋
User.where.not('nickname = ?','nil').limit(2).unscope(:limit)
=> 找到nickname不是空的user數(shù)據(jù),unscope(:limit) 把limit限制給刪除了
User.where.not('nickname=? AND realname=?','cwhh','123123').unscope(where: :realname)
=> 還可以把where的某條件刪除
空關(guān)系
User.none
=> 返回一個(gè)空 Relation 對(duì)象,而且不執(zhí)行查詢
聯(lián)結(jié)表
class Category < ApplicationRecord
has_many :articles
end
=> 一個(gè)創(chuàng)造者擁有多篇文章
class Article < ApplicationRecord
belongs_to :category
has_many :comments
has_many :tags
end
=> 一篇文章 只有一個(gè)創(chuàng)造者
=> 一篇文章 擁有多個(gè)評(píng)論
=> 一篇文章 擁有多個(gè)標(biāo)簽
class Comment < ApplicationRecord
belongs_to :article
has_one :guest
end
=> 一個(gè)評(píng)論 只有一對(duì)應(yīng)一篇文章
=> 并且一個(gè)評(píng)論只對(duì)應(yīng)一個(gè)客人
class Guest < ApplicationRecord
belongs_to :comment
end
=> 一個(gè)客人對(duì)應(yīng)一個(gè)評(píng)論
class Tag < ApplicationRecord
belongs_to :article
end
=> 一個(gè)標(biāo)簽對(duì)應(yīng)一篇文章
- 單個(gè)關(guān)聯(lián)的聯(lián)結(jié)
Category.joins(:articles)
=> 擁有文章的作者都打印出來
=> 如果這個(gè)作者有多篇文章,那么這個(gè)作者將會(huì)出現(xiàn)多次。
# 如果不想重復(fù),可以
Category.joins(:articles).distinct
- 多個(gè)關(guān)聯(lián)的聯(lián)結(jié)
Article.joins(:category, :comments)
=> 打印出屬于某個(gè)作者至少有一條評(píng)論的Article數(shù)據(jù)打印出來
=> 注意,依然會(huì)重復(fù)。
- 單層嵌套關(guān)聯(lián)的聯(lián)結(jié)
Article.joins(comments: :guest)
=> 打印出,擁有客人評(píng)論的文章數(shù)據(jù)
- 多層嵌套關(guān)聯(lián)的聯(lián)結(jié)
Category.joins(articles: [{ comments: :guest }, :tags])
=> 把擁有評(píng)論,擁有標(biāo)簽的文章創(chuàng)建者打印出來
及早關(guān)聯(lián)
- 1+n問題
clients = Client.limit(10)
clients.each do |client|
puts client.address.postcode
end
# 這里查詢 存在查詢過多的問題 每條 client 都是去找他對(duì)應(yīng)的address 和 postcode
#一共要找 1+10次address+10次postcode
# 作出改動(dòng)
clients = Client.includes(:address).limit(10)
clients.each do |client|
puts client.address.postcode
end
=> 這里一早把擁有地址的client 都找出來 然后再去找postcode,所以只執(zhí)行了2次查詢
- 多個(gè)關(guān)聯(lián)的數(shù)組
Article.includes(:category, :comments)
=> 上面的代碼會(huì)加載所有文章、所有關(guān)聯(lián)的分類和每篇文章的所有評(píng)論。
Category.includes(articles: [{ comments: :guest }, :tags]).find(1)
=> 上面的代碼會(huì)查找 ID 為 1 的分類,并及早加載所有關(guān)聯(lián)的文章、這些文章關(guān)聯(lián)的標(biāo)簽和評(píng)論,以及這些評(píng)論關(guān)聯(lián)的訪客。
- 為及早關(guān)聯(lián)指定條件
User.includes(:user_grounp).where(:user_grounp, {name: :teacher})
=> 提早關(guān)聯(lián)有只有teacher小組里的的user用戶數(shù)據(jù)
- 要想像上面的代碼那樣使用 where 方法,必須在 where 方法中使用散列。如果想要在 where 方法中使用字符串 SQL 片段,就必須用 references 方法強(qiáng)制使用聯(lián)結(jié)表:
User.includes(:user_groups).where('user_groups.name=?','teacher').references(:user_groups)
作用域
#寫在model里的
class User < ApplicationRecord
scope :sex, -> { where(sex: '男') }
end
#這兩種方法達(dá)到的效果相同
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
- 調(diào)用
user = User.first
user.sex
- 傳入?yún)?shù)
class User < ApplicationRecord
scope :created_before, ->(time) { where("created_at < ?", time) }
end
# 調(diào)用都是一樣的
user = User.first
user.created_before(Time.now)
#當(dāng)作用域需要接受參數(shù)時(shí),推薦改用類方法。使用類方法時(shí),這些方法仍然可以在關(guān)聯(lián)對(duì)象上訪問:
class Article < ApplicationRecord
def self.created_before(time)
where("created_at < ?", time)
end
end
#調(diào)用一樣的
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
- 應(yīng)用默認(rèn)作用域
class Client < ApplicationRecord
default_scope { where("removed_at IS NULL") }
end
class Client < ApplicationRecord
def self.default_scope
# 應(yīng)該返回一個(gè) ActiveRecord::Relation 對(duì)象
end
end
默認(rèn)作用域在創(chuàng)建記錄時(shí)同樣起作用,但在更新記錄時(shí)不起作用。例如:
合并作用域
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'
有一點(diǎn)需要特別注意,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')
=> 這時(shí)候存到數(shù)據(jù)庫時(shí)候 status 就是 1
=> 輸出的時(shí)候 status 就是 activation
find_or_create_by 方法
這個(gè)方法 如果找到 就返回這個(gè)數(shù)據(jù),沒有找到就 創(chuàng)建一條這樣的數(shù)據(jù)
find_or_initialize_by 方法
此方法和find_or_create_by差不錯(cuò),只不過不是創(chuàng)建時(shí)候是new,不是create,要手動(dòng)保存
使用 SQL 語句進(jìn)行查找
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)
=> 返回的是所有名字的一個(gè)組合的一個(gè)數(shù)組
檢查對(duì)象是否存在
User.exists? => true
User.exists?(id: [1,2,3]) => 只要有一條對(duì)應(yīng)記錄存在就會(huì)返回 true
User.exists?(nickname: 'cwhh') => true
User.exists?(nickname: '1231231231') => false
計(jì)算
User.count => 200