8

我正在使用 Ruby on Rails 3.2.2,我想生成以下 SQL 查询:

SELECT `articles`.* FROM `articles` WHERE (`articles`.`user_id` = 1 OR `articles`.`status` = 'published' OR (`articles`.`status` = 'temp' AND `articles`.`user_id` IN (10, 11, 12, <...>))) 

通过这种方式使用Arel

Article
 .where(
   arel_table[:user_id].eq(1)
   .or(arel_table[:status].eq("published"))
   .or(
     arel_table[:status].eq("temp")
     .and(
       arel_table[:user_id].in(10, 11, 12, <...>)
     )
  )
)

它生成以下内容(注意:括号与第一个 SQL 查询不同):

SELECT `articles`.* FROM `articles` WHERE (((`articles`.`user_id` = 1 OR `articles`.`status` = 'published') OR `articles`.`status` = 'temp' AND `articles`.`user_id` IN (10, 11, 12, <...>))) 

由于我认为后一个 SQL 查询不像第一个那样“工作”,我怎么能使用 Arel(或者,也许是别的东西)来生成 SQL 查询作为第一个?

更新(评论后)

鉴于上面的 SQL 查询“工作”相同,但我仍然想生成与问题中的第一个完全相同的 SQL 查询(这样做的主要原因是第一个 SQL 查询比第二个更易读,因为在第一个一个使用较少和“显式”括号),我怎样才能通过使用 Arel 来做到这一点?

4

3 回答 3

14

我有同样的问题。我在网上搜索了几个小时,最后在Arel::FactoryMethods中找到了一个名为grouping的方法,它只是在表达式周围添加了括号。

你应该用一个电话来包装你的小组。arel_table.grouping(...)

于 2013-03-01T16:46:59.213 回答
7

如何arel_table.grouping(...)作为范围的一部分使用的示例

# app/model/candy.rb
class Candy < ActiveRecord::Base
  has_many :candy_ownerships
  has_many :clients, through: :candy_ownerships, source: :owner, source_type: 'Client'
  has_many :users,   through: :candy_ownerships, source: :owner, source_type: 'User'

  # ....
  scope :for_user_or_global, ->(user) do
    # ->() is new lambda syntax, lamdba{|user| ....}

    worldwide_candies  = where(type: 'WorldwideCandies').where_values.reduce(:and)
    client_candies     = where(type: 'ClientCandies', candy_ownerships: { owner_id: user.client.id, owner_type: 'Client'}).where_values.reduce(:and)
    user_candies       = where(type: 'UserCandies',   candy_ownerships: { owner_id: user.id,        owner_type: 'User'  }).where_values.reduce(:and)

    joins(:candy_ownerships).where( worldwide_candies.or( arel_table.grouping(client_candies) ).or( arel_table.grouping(user_candies) ) )
  end

  # ....
end

称呼

Candy.for_user_or_global(User.last)
#=> SELECT `candies`.* FROM `candies` INNER JOIN `candy_ownerships` ON `candy_ownerships`.`candy_id` = `candies`.`id` WHERE (`candies`.`deleted_at` IS NULL) AND (((`candies`.`type` = 'WorldwideCandies' OR (`candies`.`type` = 'ClientCandies' AND `candy_ownerships`.`owner_id` = 19 AND `candy_ownerships`.`owner_type` = 'Client')) OR (`candies`.`type` = 'UserCandies' AND `candy_ownerships`.`owner_id` = 121 AND `candy_ownerships`.`owner_type` = 'User')))

thx micha给小费

于 2013-04-15T11:35:07.917 回答
0

我已经成功地使用了这个宝石:squeel,它位于 Arel 之上,所以你不必弄乱它。因此,为了生成您的查询,您可以在 Squeel 中执行以下操作:

@articles = Article.
  where{
  ( user_id.eq(1) | status.eq('published') ) |
  ( user_id.in([10, 11, 12, '<...>']) & status.eq('temp') )
}

# since this is an ActiveRecord::Relation we can play around with it
@articles = @articles.select{ [ user_id, status ] }

# and you can also inspect your SQL to see what is going to come out
puts @articles.to_sql

您的查询越复杂,您就越喜欢这个宝石。

于 2012-06-25T14:47:03.337 回答