215 Rails 3 中的高階查詢

2021-08-30 18:41:42 字數 4314 閱讀 6627

[size=x-large]用類方法代替scopes[/size]

在我們要來展示的應用程式中包括兩個模型:product 和 category,其中,product屬於category,並且在product模型中有乙個named scope: discontinued, 用來表示已經停產和價錢低於乙個給定的值的產品。

其中,在第二個named scope的描述中我們用到了lambda,如果你使用過named scope特別是有需要大量傳入引數或者是scope本身邏輯複雜的情況下,都會考慮要把scope抽出來寫到方法裡,雖然,我們當前的例子並沒有什麼複雜的,為了演示我們同樣抽成方法如下:

如上抽出方法的scope和原來的scope有同樣的功能,雖然在rails 2中同樣支援這樣的操作,然而,在rails 3中這樣的方法抽象會有更完善的功能。假設我們有另外乙個scope cheap表示價錢低於5的產品,我們就可以重用之前為了使用scope而建立的方法,構造如下:

然而,這裡有乙個潛在的陷阱,就是我們的scope方法的定義要放到其他的類定義方法的後面,也就是說帶方法的scope會在普通scope的位置靠後。

在rails 控制台我們可以看到scope對應的sql如下:

ruby-1.8.7-p249 > product.cheap.to_sql

#=> "select \"products\".* from \"products\" where (price < 5)"

直接呼叫定義的scope會顯示符合scope條件的產品。

>product.cheap

#=> [#, #]

[size=x-large]關聯[/size]

在rails控制台下我們可以展示另外乙個通過關聯使用scope的技巧。我們之前提到product和category有belongs_to的關聯,所以,也就是說我們可以使用joins來返回sql的join表的查詢

ruby-1.8.7-p249 > category.joins(:products).to_sql

#=> "select \"categories\".* from \"categories\" inner join \"products\" on \"products\".\"category_id\" = \"categories\".\"id\""

下面是關於符合特定scope的查詢的寫法,比如我們希望查詢至少有乙個產品的價值是低於5的所有種類。那麼我們可以有下面的兩種寫法,一種是用merge如下:

> category.joins(:products).merge(product.cheap)

#=> [#, #]

同樣的我們也可以使用和merge有相同含義的&來表達如下:

> category.joins(:products) & product.cheap

#=> [#, #]

使用這樣的方法我們可以關聯一些不在自己model的檢索條件,比如我們希望查詢所有產品的價值都小於5的類別如下,並且我們可以使用to_mysql方法檢視對應的mysql的語句。

> (category.joins(:products) & product.cheap).to_sql

#=> "select \"categories\".* from \"categories\" inner join \"products\" on \"products\".\"category_id\" = \"categories\".\"id\" where (price < 5)"

更為強大的是,我們可以在named scope定義中使用是定義好的scope就如同我們剛剛用到的。比如,建立乙個包括所有cheap 產品的類別,如下:

這兒named scope會返回和我們之前的查詢相同的資料:

> category.with_cheap_products

# => [#, #]

有乙個值得注意的問題是關於多表關聯查詢中的表名問題,這一點可以通過檢視上面我們建立的scope的sql看到:實際上在where查詢中我們並沒有寫明特定的表名.

> category.with_cheap_products.to_sql

# => "select \"categories\".* from \"categories\" inner join \"products\" on \"products\".\"category_id\" = \"categories\".\"id\" where (price < 5)"

當兩個關聯的表都有相關的字段的時候,這就會是問題。所以,我們應該在product 模型從新定義並且指定表名,這樣就不怎麼出錯了。

也就是說,在我們自己寫sql條件的時候,為了防止不同的表之間有重複欄位的問題就應該在關聯查詢中寫明表名。當然如果是按照rails的雜湊條件寫,比如在scope裡我們定義查詢,那麼就不要我們自己指明屬於哪個表,rails會自動加上。

[size=x-large]通過named scopes建立記錄[/size]

我們可以通過named scopes建立新的記錄,例如在product模型上有乙個叫discontinued的named scope

> product.discontinued

# => [#]

因為named scope是用的hash所以,我們可以通過呼叫build方法建立discontinued屬性為真的記錄。

> p = product.discontinued.build

# => #

這種建立的方式類似於通過rails的關聯方式的建立(譯者曉夜注:例如has_many的關聯)這樣預設的情況下就會有一些限定通過外來鍵設定好。我們通過scope建立記錄是用過where語句進行的限定。

[size=x-large]arel[/size]

> t = product.arel_table

變數t代表product表,我們可以通過如下方式訪問字段屬性:

>t[:price]

# =>

通過對屬性呼叫方法等於執行條件查詢,例如,我們要找到價值為2.99美元的所有商品:

> t[:price].eq(2.99)

=> #, @operand2=2.99>

這將返回乙個predicate物件代表著查詢條件。還有一些其他條件我們可以用,比如matches代表like查詢。我們也可以通過to_sql來檢視這個查詢對應的sql語句

> t[:name].matches('%lore').to_sql

# => "\"products\".\"name\" like '%lore'"

同時我們可以使用or方法將兩個predicate物件連在一起進行組合查詢,比如查詢價值2.99美元或者產品名是lore的產品:

t[:price].eq(2.99).or(t[:name].matches('%lore'))

這將生成如下sql語句,我們可以通過to_sql看到:

> t[:price].eq(2.99).or(t[:name].matches('%lore')).to_sql

# => "(\"products\".\"price\" = 2.99 or \"products\".\"name\" like '%lore')"

因為predicate物件對應乙個查詢條件,那麼我們就可以把predicate作為乙個引數傳給activerecord的where方法。例如把上面的predicate傳給product.where,就會返回價值是2.99美元或者名字以lore結尾的記錄集。

>   product.where(t[:price].eq(2.99).or(t[:name].matches('%lore')))

# => [#, #]

product.where(:price.eq => 2.99, :name.matches => '%lore')

如果需要更採用靈活的方式進行更加複雜的查詢,你可以再進一步了解arel和metawhere相關的知識

rails中的模型關聯(高階篇)

rails中的模型關聯 高階篇 總結了一些 rails 模型關聯,有些可能不是很常見,但是會很有用,在這裡和大家分享一下。1.has many through has many 的用法大家可能都很熟悉,但是後面跟乙個 through 呢?has many through 通常表示兩個模型之間的多對多...

mysql 高階 查詢 MYSQL中的高階查詢

1.1.子查詢 1.1.1.在房屋型別中,如何找出比雙人間貴的所有房屋型別?找到雙人間的 根據第一步找到的 作為查詢條件去查滿足條件的房屋型別,利用where字句 子查詢是乙個巢狀在 select insert update 或 delete 語句或其他子查詢中的查詢 子查詢在where語句中的一般...

Rails中的時區和Mysql查詢問題

如果,在你的rails專案中資料庫的設定是如下 config.active record.default timezone utc rails2.1 config.time zone utc rails 2.3 那麼通常情況下,這時你就要注意你的rails查詢中的時區問題了。例如,通常的查詢如下 u...