✅How to find records using a variety of methods and conditions.html
✅How to specify the order, retrieved attributes,grouping, and other properties of the found records.sql
✅ How to use eager loading(預先積極加載) to reduce the number of database queries needed for data retrieval.數據庫
✅ How to use dynamic finder methods.api
✅ How to use method chaining to use multiple Active Record methods together.✅ How to check for the existence of particular records數組
✅ How to perform to do sth useful or difficult various calculations on Active Record models.ruby
✅ How to run EXPLAIN on relations.(如何在關聯上跑explain命令) app
Active Record will perform queries on the database for you and is compatible(兼容) with most database systems, including MySQL,SQLite ,etc..ide
1 .Retrieving Objects from the Databasepost
The methods are: find ,where,select,group,20多種。ui
Finder methods that return a collection, such as where and group, return an instance of ActiveRecord::Relation.
Methods that find a single entity, such as find and first, return a single instance of the model.
The primary operatin of Model.find(options) can be summarized as:
1.1Retrieving single objects
1.11 find
經過指定primary key來檢索object。能夠同時檢索多個primary key ,並返回一個數組,數組內包含因此匹配的記錄。例子:
client = Client.find([1,10]) //或者寫Client.find(1,10)
#=> [#<Client id: 1, first_name: "Lifo">, #<Client id: 10, first_name: "Ryan">]
等同於:
SELECT * FROM clients WHERE(clients.id IN (1,10))
若是primary key沒有匹配的記錄,find method會拋出ActiveRecord::RecordNotFound異常
1.12 take(數字)
The take method retrieves a record without any implicit ordering .例子:
參數是幾,就返回幾條記錄。
client = Client.take
# => #<Client id: 1, first_name: "Lifo">
|
等同於:
SELECT
*
FROM
clients LIMIT 1
|
若是沒有檢索到哪怕一條數據就會返回 nil.
1.13 first
find the first record ordered by primary key(default).
若是沒有檢索到哪怕一條數據就會返回 nil.能夠傳入數字參數,根據數字,返回對應的記錄數量。
能夠和order連用。
first! ,if no matching record is found, it will rasie ActiveRecord::RecordNotFound
1.14 last
用法和first同樣,sql語句是按照 DESC 降序排列。
1.15 find_by
finds the first record matching some conditions.例子:
Client.find_by first_name: 'XXX'
等同於
Client.where(first_name: 'xxx').take //take是取where找到的第一個數據。
1.2 Retrieving Multiple Objects in Batches
分批取回大量對象,屢次檢索,每次檢索的記錄數量有限制。默認1000條。
當dadabase很是大的時候,User.all.each 。。end 會佔用超大內存,和時間。因此Rails提供了2個method:
find_each和find_in_batches,1000條以上記錄,用這個比較好。batch processing 批處理
1.2.1 find_each 方法
User.find_each(可選參數)
do
|user|
NewsMailer.weekly(user).deliver_now
end
|
有3個options for find_each:
find_in_batches()方法,和find_each方法的區別
find_in_batches()每次返回的是一個數組,裏面是模型對象的集合。
find_each()每次返回的是獨立數量的模型對象,沒有用數組括起來。
2 conditions --where()
不要用pure string,容易被injection exploits.注射攻擊剝削。
㊗️:find和find_by method在ruby和rails 中已經自動能夠規避這個問題。
攻擊方法:在where的查詢中添加一些string(有多種方法),來繞過登錄驗證,獲取數據 。
⚠️ :where, find_by_sql, connection.execute()等條件片斷的查詢,須要手動避免注入。 好的方法就是隻用數組,或hash.
Returns a new relation, which is the result of filtering the current relation according to the conditions in the arguments
2.2 Array Conditions
Clinet.where("orders_count = ?", params[:orders])
Client.where("orders_count = ? AND locked = ?", params[:orders], false)
placeholder, 能夠在條件字符串中指定keys,以後附加相關的keys/values對兒。
Client.where("created_at >= :start_date AND created_at < :end_date", {start_date: params[:start_date], end_date: params[:end_date]})
2.3 Hash Condititons 具體看api
能夠用Equality Conditions,Range Conditions, Subset Conditions.
Client.where(locked: true)
-> select * from clients where(clients.locked = 1)
Client.where(created_at: (Time.now.midnight - 1.day)..Time.now.midnight)
->SELECT
*
FROM
clients
WHERE
(clients.created_at
BETWEEN
'2008-12-21 00:00:00'
AND
'2008-12-22 00:00:00'
)
Client.where(orders_count: [1,3,5])
-> SELECT
*
FROM
clients
WHERE
(clients.orders_count
IN
(1,3,5))
在 belongs_to relationship, an association key can be used to specify the model if an ActiveRecord object is used as the value.
for example:
author = Author.find(1); // author 被定義爲一個關聯的對象
Post.where(author_id: author) //也能夠寫成:where(author: author) 但前者容易理解。
NOT
Client.where.not(locked: true)
-> select * from clients where(clients.locked != 1)
OR
Client.where(locked: true).or(Client.where(orders_count: [1,3,5]))
->select * from clients where(clients.locked = 1 or clients.orders_count in (1,3,5))
3 Ordering
Client.order(created_at:
:desc
)
至關於MySQL:
SELECT * from client ORDER BY created_at ASC;
Client.order("orders_count ASC").order("created_at DESC")
-> select * from clients order by orders_count asc, created_at desc
4 Selecting Specific Fields
select用於從結果集中選擇字段的子集,就是隻撈出column "vewable_by"和「locked」的值,其餘列不撈出。
Client.select(
"viewable_by, locked"
)
|
等同於
SELECT
viewable_by, locked
FROM
clients
|
5 Limit and Offset
You can use limit to specify the number of records to be retrieved,
Client.limit(5) 等同於 SELECT * FROM clients LIMIT 5
若是使用offset
Client.limit(
5
).offset(
30
)將返回5條記錄,從31st開始。
⚠️ :用offset必須先用limit
6
Group
To apply a Group by clause to the SQL fired by the finder, you can use the group method.
Order.select("date(created_at) as ordered_date, sum(price) as total_price").group("date(created_at)")
->
select date(created_at) as ordered_date, sum(price) as total_price FROM orders GROUP BY date(created_at)
7 Having方法
用having進行group by的條件限制。例子:
Order.select(
"date(created_at) as ordered_date, sum(price) as total_price"
).
group(
"date(created_at)"
).having(
"sum(price) > ?"
,
100
)
|
等同於:
SELECT
date
(created_at)
as
ordered_date,
sum
(price)
as
total_price
FROM
orders
GROUP
BY
date
(created_at)
HAVING
sum
(price) > 100
This returns the data and total price for each order object, grouped by the day they were created and where the price is more than $100.
上面的語句返回每一個order對象的日期和總價,查詢結果按照日期分組並排序(合併?),而且總價須要大於100.
8 Overriding Conditions 覆蓋條件(對條件進行限制)
8.1 unscope
移除指定的條件。
Article.where(
'id > 10'
).limit(
20
).order(
'id asc'
).unscope(
:order
)等同於:
SELECT
*
FROM
articles
WHERE
id > 10 LIMIT 20
|
8.2 only
指定用哪一個查詢條件。 和unscope正相反。
8.3 reorder :
The reorder method overrides the default scope order.當創建了一個一對多關聯並指定排列的順序後,想要在查詢某個一對多的對象的實例上重新排序(不使用在model中申明的):能夠用reorder.
class
Article < ApplicationRecord
has_many
:comments
, -> {order(
'posted_at DESC'
) }
end
Article.find(
10
).comments.reorder(
'name'
)
|
SQL語句會被執行executed:
SELECT
*
FROM
articles
WHERE
id = 10
SELECT
*
FROM
comments
WHERE
article_id = 10
ORDER
BY
name
|
8.4 reverse_order 配合reorder或者order,升序變降序。
Article.none
# returns an empty Relation and fires no queries.返回nil, []
|
當一個對象被設置爲只讀,他的屬性不能被修改。
client = Client.readonly.first
client.visits += 1
client.save #這時會報錯❌,raise ActiveRecord::ReadOnlyRecord exception
積極鎖,和消極鎖。 防止在數據庫更新時,出現混亂狀況。
(沒仔細看。)
積極鎖容許多個用戶同時更新同一條記錄,並假定產生數據衝突的可能性最小。其原來是檢查讀取記錄後看是否有其餘進程嘗試更新記錄,若是有就拋出異常。
爲了使用樂觀鎖,表格有一個整數型字段lock_version。每次記錄更新,會同步增長這個字段的值。 若是更新請求中字段的值比當前數據庫的字段的值小,更新請求失敗,拋出異常。
拋出異常後,須要救援異常並處理衝突,或回滾或合併或使用其餘邏輯來解決衝突。
12 Joining Tables 聯結表
連個方法:
1 使用字符串 SQL 片斷
Author.joins("INNER JOIN posts ON posts.author_id = authors.id")
->
SELECT authors.* FROM authors INNER JOIN posts ON posts.author_id = authors.id
2 使用 Array/Hash of Named Associations使用具名關聯數組或散列
若是連個model已經創建了關聯:
Category.joins(:articels)
-》
select categories.* from categories inner join articles on articles.category_id = categories.id
會返回一個包含category 對象的數組,如過有多個article和一個category關聯,則返回的這個數組中會出現屢次這個category對象。
能夠附加destinct方法 ,這樣重複的category對象不會出現了。
3. 多個關聯的聯結
category has_many :articles
articles has_many :comments
Article.joins(:category, :comments)
->
select articles.* from articles
inner join categories on articles.category_id = categories.id
inner join comments on comments.artice_id = articles.id
解釋:把屬於某個目錄並至少有一條評論的文章做爲一個Article對象返回。
一樣,擁有多個評論的一篇文章會在Article對象的數組中出現屢次。
4單層嵌套關聯的聯結⚠️ 沒細看
Article.joins(comments: :guest)
5 爲joining table 指明條件
可使用array, string條件 做爲關聯數據表的條件。
散列須要特殊的語法:
time_range = (Time.now。midnight - 1.day)..Time.now.midnight
Client.joins(:orders).where("orders.created_at" => time_range)
或者
Client.joins(:orders).where(orders: {created_at: time_range})
select clients.* from clients
inner join orders on clients.order_id = orders.id
where orders.created_at between "XXX時間" and "xxx時間"
解釋:查找昨天建立過訂單的全部客戶,在生成的SQL中使用了between..and..
一種查找機制:目的是減小查詢數據庫的次數。
client has_one :address
clients = Client.limit(10)
clients.each do |c|
puts c.address.postcode
end
先查詢10條clients記錄,而後每條記錄執行關聯查詢,一個調用了數據庫11次。
爲了提升效能:使用includes()方法。
clients = Client.includes(:address).limit(10)
這行代碼執行了2次數據庫。
1. select * from clients limit 10
2. select addresses.* from addresses where(addresses.client_id IN (1,2,3,..10))
把經常使用的查詢定義爲方法,能夠在關聯對象或模型上做爲方法調用。
總會返回一個ActiveRecord::Relation object
scope :published, -> {where(published: ture)}
徹底等同於類方法:
def self.publish
where(published: true)
end
在做用域中能夠連接其餘scope。
如:
Article.published #做爲類方法使用
category.articles.published #在關聯對象上調用。
14.1 傳遞參數。
scope :created_before, ->(time) {where("created_at < ?", time)}
14.2 能夠在{}內使用 if.
謹慎使用,若是if 的結果是false,會返回nil,並致使NoMethodError❌
14.3 默認做用域
default_scope:在模型的任意查詢中會自動附加上默認做用域、
default_scope {where("removed_at IS NULL")}
⚠️更新記錄時,不會加上這個方法。
⚠️default_scope方法總在自定義的scope和where方法前起做用
14.4 合併做用域
兩個scope鏈接起來,至關於使用了AND來合併做用域。
14.5 unscoped方法:刪除全部的做用域。
Client.unscoped.all只執行常規查詢 -》 select * from clients
Client.where(published:false).unscoped.all
不會執行where條件查詢,把where當成了scope.
find_by_*()方法。其實就是find_by(key/value)
給一個類型是integer的列,指定一組值做爲選項。
create_table :conversations do |t| t.column :status, :integer, default: 0 end
class Conversation < ActiveRecord::Base
enum status: { active: 0, archived: 1 }
end
Person
.select('people.id, people.nam')
.joins(:comments)
.where('comments.created_at > ?', 1.week.ago)
等同
SELECT people.id, people.name
FROM people
INNER JOIN comments ON comments.people_id = people.id
WHERE comments.created_at > "2015-01-01"
19 可使用徹底的SQL:
find_by_sql() ,返回對象的數組。
sellect_all(),和find_by_sql()是近親,區別是返回的是散列構成的數組,每行散列表示一條記錄,沒有被實例化。
pluck(),返回查詢字段的值的數組。
Client.where(active: true) .pluck(:id)
-> select id from clients where active =1 #=> [1, 2, 3]
pluck()方法至關於使用select().map{|x| x.attr}
Client.select(:id, :name).map{|c| [c.id, c.name]}
-> Client.pluck(:id, :name)
⚠️,pluck()返回的是數組,因此不能放在查詢方法鏈的中間,只能放置在最後。
20 ids方法
得到關聯的全部ID, 數據表的primary_key
collection_singular_ids 這是has_many中的選項。返回關聯對象的id的數組。\
@book_ids = @author.book_ids
21 exists?()方法,看是否存在記錄。
也能夠擁有關聯記錄collections.exists?()
也能夠用於表的查詢: 返回true/false,能夠接受多個參數,
Client.exists?(1) #id等1的客戶是否存在。
any? 是否存在一條記錄
many? 是否存在2條(含)以上的記錄。
21 calculation
Client.count
-> select count(*) as count_all from clients 算有多少條記錄
Client.where(first_nam: "Tom").count
-> select count(*) as count_all from clients where(first_name = 'Tom')
算名字是Tom 的記錄的數量。
Client.count(:age),返回有age字段的記錄的數量
Client.average("orders_count") , 返回字段的平均值
Client.minimum("age")
Client.maximum("age")
Client.sum("orders_count")