我需要运行像
这样的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开发人员,但我想将这种孩子语言改为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查询构建器这样的查询构建器构建的查询更具可读性和可理解性。
有没有宝石可以解决这个问题?
答案 0 :(得分: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%'))
注意使用块:{...}
这里不是哈希。另请注意没有符号。
如果你决定选择它,请阅读以“这带来一个重要含义”开头的部分
答案 1 :(得分: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(',') }")
答案 2 :(得分: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会把它们重新组合在一起。这允许您在模型类中使用范围并将它们链接在一起。
例如,你可以这样做:
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)
等等。
将查询分解为更小的部分也可以更容易地使用您的rspec测试它的特定部分。它产生了更多模块化,可维护的代码。