2012-05-11 108 views
4

我需要運行SQL查詢像Rails的SQL查詢生成器......還是ActiveRecord的查詢生成器

sql = 'SELECT * FROM users WHERE id != ' + self.id.to_s + ' AND id NOT IN (SELECT artner_id FROM encounters WHERE user_id = ' + self.id.to_s + ')' 
sql += ' AND id NOT IN (SELECT user_id FROM encounters WHERE partner_id = ' + self.id.to_s + ' AND predisposition = ' + Encounter::Negative.to_s + ')' 
sql += ' AND cfg_sex = ' + self.sex.to_s + ' AND cfg_country = ' + self.country.to_s + ' AND cfg_city = ' + self.city.to_s 
sql += ' ORDER BY rand() LIMIT 1' 

它可以通過AR.find_by_sql執行,但之前的代碼是壞的可讀性。 是否有任何查詢生成器可以構建該查詢?

例如,Kohana的(這是PHP框架,我PHP開發人員,但我想改變孩子的語言來的Ruby/Rails)有一個查詢生成器,它的工作原理是這樣的:

$sql = DB::select('*')->from('users'); 
$sql->where('id', 'NOT_IN', DB::expr('SELECT partner_id FROM encounters WHERE user_id = '.$user->id)); 
$sql->where('id', 'NOT_IN', DB::expr('SELECT user_id FROM encounters WHERE partner_id = '.$user->id.' AND predisposition = '.Encounter::Negative)); 
.... 
etc 
... 

使用查詢生成器(如Kohana查詢生成器)構建的查詢更具可讀性和可理解性。

有沒有什麼寶石可以解決這個問題?

回答

4

您需要squeel寶石。它擴展了 AR塊和輕鬆地使非常複雜的查詢。

只是幾個特點:

# not_in == cool!) 
Product.where{id.not_in LineItem.select{product_id}} 
# SELECT "products".* FROM "products" WHERE "products"."id" NOT IN 
# (SELECT "line_items"."product_id" FROM "line_items") 

# outer joins on pure Ruby: 
LineItem.joins{product.outer} 
# LineItem Load (0.0ms) SELECT "line_items".* FROM "line_items" 
# LEFT OUTER JOIN "products" ON "products"."id" = "line_items"."product_id" 

# calcs, aliasing: 
Product.select{[avg(price).as(middle)]} 
# SELECT avg("products"."price") AS middle FROM "products" 

# comparison 
Product.where{id != 100500} 
Product.where{price<10} 

# logical OR 
Product.where{(price<10) | (title.like '%rails%')} 
# SELECT "products".* FROM "products" WHERE (("products"."price" < 10 OR 
# "products"."title" LIKE '%rails%')) 

# xxx_any feature (also available xxx_all) 
Product.where{title.like_any %w[%ruby% %rails%]} 
# SELECT "products".* FROM "products" WHERE (("products"."title" LIKE '%ruby%' OR 
# "products"."title" LIKE '%rails%'))  

注意使用塊:{...}這裏不是哈希值。還要注意沒有符號。

如果您決定選擇它,請閱讀「這所攜帶的重要含義」

3

有一個ruby庫利用關係代數。它被稱爲ARel。如果你使用的是Rails 3.x,那麼你已經擁有了。

ids = Partner.where(user_id: self.id).pluck(:partner_id) << self.id 
users = User.where("id NOT IN #{ ids.join(',') }") 
+0

它可以粘貼分貝表達式查詢,來樣訂做查詢開頭的部分「SELECT * FROM用戶WHERE ID !='+ self.id.to_s +'AND ID NOT IN(SELECT artner_id FROM encounters WHERE user_id ='+ self.id.to_s +')'? –

+0

@Zhirayr我編輯了答案 – gmile

+0

爲什麼不在這種情況下進行左連接? –

3

下面是與AREL術語相同的查詢投射。這還不算漂亮 - 這是一個複雜的查詢。

User.where("id = ? AND " 
      "id NOT IN (SELECT artner_id FROM encounters WHERE user_id = ?) AND " + 
      "id NOT IN (SELECT user_id FROM encounters WHERE partner_id = ? AND predisposition = ?) AND " + 
      "cfg_sex = ? AND cfg_country = ? AND cfg_city = ?)", 
      self.id, self.id, self.id, Encounter::Negative, 
      self.sex, self.country, self.city).order(" rand() ").limit(1) 

(我沒有測試過這一點,所以有可能有可能在它被錯字的。)

我推薦幾件事情:

當你有複雜的WHERE子句他們可以被鏈接在一起,AREL會將它們重新組合在一起。這允許您在模型類中使用範圍並將它們鏈接在一起。

例如,你可以這樣做:

User < ActiveRecord::Base 
    self.def in_city_state_country(city, state, country) 
    where("cfg_sex = ? AND cfg_country = ? AND cfg_city = ?", city, state, country) 
    end 
    self.def is_of_sex(sex) 
    where("cfg_sex = ?", sex) 
    end 
end 

然後,你可以重寫查詢,這些部分是這樣的:

User.is_of_sex(user.sex).in_city_state_country(user.city, user.state, user.country) 

等。

將查詢分解爲更小的部分也使得使用rspecs更容易地測試它的特定部分。它會產生更多模塊化,可維護的代碼。

有關詳細信息,請查看Rails Guide - Active Record Query Interface