5

私は次のようなSQLクエリを実行する必要があります

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開発者ですが、そのkid-languageを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
...

コハナクエリビルダーのようなクエリビルダーで構築されたクエリは、より読みやすく、理解しやすくなっています。

この問題を解決するための宝石はありますか?

4

3 に答える 3

4

リレーショナル代数を利用する 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(',') }")
于 2012-05-11T11:32:11.140 に答える
3

Rails の 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 は通常、それらをうまく元に戻します。これにより、モデル クラスでスコープを使用し、それらを連鎖させることができます。

たとえば、次のようにします。

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

  def self.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)

等々。

クエリを小さな部分に分割すると、rspec を使用してその特定の部分を簡単にテストすることもできます。これにより、よりモジュール化された保守可能なコードが得られます。

詳細については、Rails ガイド - Active Record Query Interfaceを参照してください。

于 2012-05-11T12:40:56.240 に答える