我拼命想弄清楚Arel,主要是因为我讨厌处理SQL;我做得很好,但是我已经撞墙了。
我一直在使用Rails 3.0.0,我正在尝试使用一些数学函数进行复杂的查询。真实情况相当复杂,但我简化了一下。在我的示例中,我有一个带有特定字符串字段的表,我想要计算所有记录,以及该字段的两个可能值中的每一个的计数,按外来ID分组。
在Rails 3.0.0下,我可以这样做(控制台命令):
t = Arel::Table.new(:some_thingies)
e = t .project(t[:foreign_id], t[:foreign_id].count.as('all_count')) .group(t[:foreign_id])
c = t.where(t[:some_field].eq('type1')).project(t[:foreign_id], t[:foreign_id].count.as('type1_count')).group(t[:foreign_id])
x = e
x = x.join(c).on(e[:foreign_id].eq(c[:foreign_id]))
此时我可以做x.to_sql而且......好吧,我不完全确定它是对的,但结果看起来是正确的,除了两次使用foreign_id列。
SELECT
`some_thingies_external`.`foreign_id`,
`some_thingies_external`.`all_count`,
`some_thingies_external_2`.`foreign_id`,
`some_thingies_external_2`.`type1_count`
FROM
(SELECT
`some_thingies`.`foreign_id`, COUNT(`some_thingies`.`foreign_id`)
AS `type1+count`
FROM `some_thingies`
GROUP BY `some_thingies`.`foreign_id`) `some_thingies_external`
INNER JOIN
(SELECT `some_thingies`.`foreign_id`, COUNT(`some_thingies`.`foreign_id`)
AS `type1_count`
FROM `some_thingies`
WHERE `some_thingies`.`type` = 'type1'
GROUP BY `some_thingies`.`foreign_id`) `some_thingies_external_2`
ON `some_thingies_external`.`foreign_id` = `some_thingies_external_2`.`foreign_id`
到目前为止一切顺利。但是,当我尝试加入第二组这样的计数时:
i = t.where(t[:some_field].eq('type2')).project(t[:foreign_id], t[:foreign_id].count.as('type2_count')).group(t[:foreign_id])
x = x.join(i).on(e[:foreign_id].eq(i[:foreign_id]))
它只是挂断了,让我觉得我正在点击this bug
(顺便说一句,我有更多的计数要加入,理想情况下'some_thingies'应该是一个arel对象,代表更多过滤我们正在计算的东西......但我离题了......)
所以,我决定尝试最新的Arel和Rails边缘,并相应地提升我的宝石:
gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'rack', :git => 'git://github.com/rack/rack.git'
gem 'arel', :git => 'http://github.com/brynary/arel.git'
现在当我尝试做第一次加入时,它失败了:
ruby-1.9.2-preview3 > x = x.join(c).on(e[:foreign_id].eq(c[:foreign_id]))
NoMethodError: undefined method `[]' for #<Arel::SelectManager:0x00000104311e38>
from (irb):12
from /Users/stephan/.rvm/gems/ruby-1.9.2-preview3/bundler/gems/rails-c42ea2172eb9/railties/lib/rails/commands/console.rb:44:in `start'
from /Users/stephan/.rvm/gems/ruby-1.9.2-preview3/bundler/gems/rails-c42ea2172eb9/railties/lib/rails/commands/console.rb:8:in `start'
from /Users/stephan/.rvm/gems/ruby-1.9.2-preview3/bundler/gems/rails-c42ea2172eb9/railties/lib/rails/commands.rb:33:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
这似乎不是Arel中的一个错误 - 如果有的话,它似乎更像是它以前工作的事实是错误。我想我只是不知道Arel :: SelectManager是什么以及如何处理它。看起来我做得很好,但我真的不知道发生了什么。
我是否需要以某种方式根据我得到的SelectManager创建一个新表?或者我在配置中做错了什么导致[]语法失败?或者我完全不理解Arel的作用?我仍然不太了解我应该用Arel :: Rows做什么,但我想我会得到那个;我怀疑我可以通过project()...
来删除结果中的额外外键但我还是很迷茫。 Haaaaalp!
P.S。用“弱点”或“邮件人”做“铁路”押韵吗?
答案 0 :(得分:3)
我将回答我自己的问题,因为似乎没有人感兴趣,现在我知道我做错了什么,我可以看到,如果我理解SQL,那将是显而易见的。
问题1与我使用Arel的方式是你只能加入一个新制作的表。那就是重点。
真正的问题是我试图计算两件不同的事情。我真的应该通过外国ID和'some_field'进行分组。我只是不知道你能做到这一点,它的结果有点奇怪。如果我不关心some_field的所有可能值,这可能很烦人,但我确实关心它们,并且我可以轻松地将它们添加到足以得到总数,并且现在很容易将它们过滤掉。 / p>
t = Arel::Table.new(:some_thingies)
e = t.group(:foreign_id, :some_field).project(t[:id], t[:foreign_id], t[:some_field])
一旦我弄清楚了,我想出了如何使用普通的ActiveRecord而不是ARel:
SomeThing.group('foreign_id, some_field').select('id, foreign_id, some_field, count(1)')
D'哦!道德:SQL只知道行。周期。
答案 1 :(得分:3)
Arel已经从内心完全重做。这项计划是由招标(Aaron)启动的。在大量撰写的查询中存在性能问题。
我自己甚至为这项新计划做出了贡献。
Arel现在使用抽象语法树(在Select Manager中)和访问者模式。
你可能会废弃Arel 1.0.1在2.0.0版本中工作的方式(在3.0.x的路上与rails对齐)