Active Record Query Interface 數據查詢接口(界面) 看到第8節。

  • http://guides.rubyonrails.org/active_record_querying.html


✅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: 

  • 根據options轉化成SQL語句,激發查詢語句並從數據庫返回結果
  • 從數據庫返回的模型的每行結果實例化爲相關的Ruby對象
  • Run after_find and then after_initialize callbacks, if any.

 


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.

能夠傳入數字參數,根據數字,返回對應的記錄數量。

Product.first(2) 等同於 
SELECT  "products".* FROM "products" ORDER BY "products"."id" ASC LIMIT ?  [["LIMIT", 2]]

能夠和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_eachfind_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:

  • :batch_size: 指定每次檢索返回多少條數據.
  • :start and :finish 根據primary id,肯定開始和結束。


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

 


 

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,升序變降序。

 


 

 

9 Null Relation

Article.none # returns an empty Relation and fires no queries.返回nil, []

 

10 Readonly Objects

當一個對象被設置爲只讀,他的屬性不能被修改。 

client = Client.readonly.first

client.visits += 1

client.save  #這時會報錯❌,raise ActiveRecord::ReadOnlyRecord exception 

 


11 Locking Records for Update

積極鎖,和消極鎖。 防止在數據庫更新時,出現混亂狀況。

(沒仔細看。)

積極鎖容許多個用戶同時更新同一條記錄,並假定產生數據衝突的可能性最小。其原來是檢查讀取記錄後看是否有其餘進程嘗試更新記錄,若是有就拋出異常。

爲了使用樂觀鎖,表格有一個整數型字段lock_version。每次記錄更新,會同步增長這個字段的值。 若是更新請求中字段的值比當前數據庫的字段的值小,更新請求失敗,拋出異常。

拋出異常後,須要救援異常並處理衝突,或回滾或合併或使用其餘邏輯來解決衝突。

 


12 Joining Tables 聯結表

連個方法:

  1. joins: 對應SQL的 INNER JOIN ...ON
  2. left_outer_joins: 對應LEFT OUTER JOIN 

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..

 


13 Eager Loading Associations

一種查找機制:目的是減小查詢數據庫的次數。

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)) 

 


 

14 Scopes

把經常使用的查詢定義爲方法,能夠在關聯對象或模型上做爲方法調用。 

總會返回一個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. 

 


15 Dynamic Finders

find_by_*()方法。其實就是find_by(key/value)

 


16 enum

 給一個類型是integer的列,指定一組值做爲選項。

create_table :conversations do |t|
  t.column :status, :integer, default: 0
end
class Conversation < ActiveRecord::Base
 enum status: { active: 0, archived: 1 }
end

 

 


17. Understanding the chain方法鏈鏈接多個ActiveRecord方法。

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") 

相關文章
相關標籤/搜索