[Rails] Active Record Query(SQL Query & Model 資料查詢)
keywords: SQL Query
, scope
, enum
, find_or_create_by
Active Record Query Interface @ RailsGuides
TL;DR
Post.where('is_published = ? AND publish_at <= ?', true, Time.zone.now)
目錄
[TOC]
取得資料(Retrieving Objects from the Database)
取得單一物件(Retrieving a Single Object)
keywords: find
, find_by
, where
, first
, last
, take
# find: 透過 Primary Key 搜尋資料
product = Product.find(10)
products = Product.find([1, 10]) # 等同於 Client.find(1, 10)
# find_by: 根據欄位找出第一筆符合的資料
product = Product.find_by name: 'car' # 找出第一筆欄位 name 中值為 car 的資料
product = Product.where(name: 'car').take # 和上面的寫法相同
# first: 根據 id 排序,取得第一筆資料
product = Product.first # 根據 id 排序取得第一筆資料
products = Product.first(3) # 根據 id 排序取得前三筆資料
# last:根據 id 排序,取得最後一筆資料
product = Product.last # 根據 id 排序取得最後一筆資料
products = Product.last(3) # 根據 id 排序取得倒數後三筆資料
# take: 取得資料
product = Product.take # 取得一筆 Product 的資料
products = Product.take(2) # 取得兩筆 Product 的資料
# 其他
Candidate.count # 計算資料筆數
Candidate.average(:age).to_f # 根據某資料平均
Candidate.sum(:age) # 根據某資料加總
Candidate.maximum(:age) # 取的最大值
Candidate.minimum(:age) # 取的最小值
分批取得多筆資料(Retrieving Multiple Objects in Batches)
keywords: find_each
, find_in_batches
在取得多筆資料時我們很容易會使用:
Products.all.each do |product|
#...
end
但這樣的做法,會一次將整個 table 的資料提取出來,並存放在記憶體中,因此這個做在當資料量很龐大時,很有可能會超過記憶體的負荷。
Rails 提供兩種方式來改善記憶體潰乏的問題,這兩種方式都對記憶體的使用更為友善,分別是 find_each
和 find_in_batches
。
條件篩選(Conditions)
基本的條件篩選
keywords: where
# !!非常不建議這樣寫!!
# 使用純字串篩選(Pure String Conditions)
Client.where("orders_count = '2'") # 非常不建議這樣寫
Client.where("first_name LIKE '%#{params[:first_name]}%'") # 非常不建議這樣寫
Client.where("orders_count = #{params[:orders]}") # 非常不建議這樣寫
# 使用 ? (Array Conditions)
# Active Record 會把第一個參數當作條件,後面的參數則會分別取代掉前面的 (?)
Client.where("orders_count = ?", params[:orders]) # 單一條件
Client.where("orders_count = ? AND locked = ?", params[:orders], false) # 多個條件
# 使用 Symbol 定義變數,再透過 Hash 給值(placeholder conditions)
Client.where("created_at >= :start_date AND created_at <= :end_date",
{start_date: params[:start_date], end_date: params[:end_date]})
# 使用 Hash
Client.where(locked: true)
Client.where('locked' => true)
Client.where(status: :active) # 錯誤寫法,Hash 的值不能是 Symbol
注意: > 使用單純 的字串來定義條件很容易陷入 SQL injection 的危險中。 > 千萬不要把「參數」直接帶入條件篩選的字串內!
因為參數的安全性,把變數直接帶到條件式中,等同於直接把使用者輸入的內容如實的帶入資料庫查詢中,因此即使使用者輸入非法字元也不會被跳脫掉,這將使得你的資料庫面臨危險。
搜尋功能
ILIKE
是 postgreSQL 的擴充功能,表示針對字串進行 insensitive(忽略大小寫)的比較。
# 非常不建議這樣寫
App::Completion.joins(:personal_info).where('name ILIKE ? OR country ILIKE ? OR city ILIKE ?', "%#{search}%", "%#{search}%", "%#{search}%")
# 非常不建議這樣寫
App::Completion.joins(:personal_info).where("name ILIKE '%tester%'").all