Rails ActiveRecord 复杂连接问题 - Select 不起作用

发布于 2024-08-08 19:42:05 字数 2244 浏览 5 评论 0原文

我有两个模型对象:

  • 活动
  • 场地

活动有场地。场地可以举办 1..* 场活动。

场地有一个位置、纬度和经度,我将其与 Geokit Rails 插件一起使用。这些模型在 Rails 中的样子如下:

class Event < ActiveRecord::Base
  belongs_to :venue
  acts_as_mappable :through => :venue
end

非常

class Venue < ActiveRecord::Base
  has_many :events
  acts_as_mappable  
end

简单!现在,我想运行一个查询,我想找到某个区域几英里内的所有事件,查看 Geokit API,我发现我可以使用 mappable 与事件上的 :through 关联,并且应该能够在活动中调用 geokit 查找器!非常好(所以你可以在上面看到,我已将 actions_as_mappable :through 添加到事件中)。

这是我的 ActiveRecord 查询:

Event.find(:all, :origin => [lat, lng], :select => 'title', :within => 5, :limit => 10)

这是生成的 SQL:


SELECT `events`.`id` AS t0_r0, `events`.`title` AS t0_r1, `events`.`description` AS t0_r2,
 `events`.`created_at` AS t0_r3, `events`.`updated_at` AS t0_r4, `events`.`venue_id` AS 
t0_r5, `events`.`event_detail_id` AS t0_r6, `events`.`event_detail_type` AS t0_r7, 
`venues`.`id` AS t1_r0, `venues`.`name` AS t1_r1, `venues`.`lat` AS t1_r2, `venues`.`lng` 
AS t1_r3, `venues`.`created_at` AS t1_r4, `venues`.`updated_at` AS t1_r5 FROM `events` LEFT
 OUTER JOIN `venues` ON `venues`.id = `events`.venue_id WHERE 
(((venues.lat>51.4634898826039 AND venues.lat>51.5533406301823 AND venues.lng>-
0.197713629915149 AND venues.lng<-0.053351862714855)) AND 
((ACOS(least(1,COS(0.898991438708539)*COS(-0.00219095974226756)*COS(RADIANS(venues.lat))*COS(RADIANS(venues.lng))
 COS(0.898991438708539)*SIN(-
0.00219095974226756)*COS(RADIANS(venues.lat))*SIN(RADIANS(venues.lng))
 SIN(0.898991438708539)*SIN(RADIANS(venues.lat))))*6376.77271)
 <= 5)) LIMIT 10

哎呀。好吧,这需要相当长的时间才能运行,而且这不仅仅是数学问题,我认为这里存在一个严重的性能问题,在我的桌面上运行需要 2 秒!但说实话,我的抱怨是:

  • 为了尝试提高性能,您可以看到我尝试在 ActiveRecord 查找查询中使用 SELECT 语句。我只想知道这些匹配的事件的标题,而不是所有其他字段。但是查询通过了,并从事件中选择了所有内容!它使用我不熟悉的奇怪别名,例如“t1_r1”。所以这显然是一个问题。

此外,当单独针对场所运行时(即取消 JOIN),该查询的执行时间不到 10 毫秒。那么有没有办法先进行场地搜索,然后再加入活动呢?我认为连接必须在两个表上作为一个整体完成,然后地理编码部分才会介入并缩小数据集。

任何帮助收紧这个问题的帮助都将非常感激(这是一个非-商业项目)。我觉得这个模型很简单我应该能够做到这一点而不需要太多麻烦?

提前致谢!

(编辑:在这篇文章的早期版本中,我很愚蠢,Limit 工作得很好,我的观点被打破了,经过编辑以删除该部分。)

I have two model objects:

  • Event
  • Venue

Events have Venues. Venues can have 1..* events.

Venues have a location, a lat and long, which I use with the Geokit Rails plugin. Here's what these models look like in Rails:

class Event < ActiveRecord::Base
  belongs_to :venue
  acts_as_mappable :through => :venue
end

and

class Venue < ActiveRecord::Base
  has_many :events
  acts_as_mappable  
end

Very simple! Now, I'd like to run a query, I'd like to find all the events within a few miles of a certain area, looking at the Geokit API, I see that I can use mappable with a :through association on Event, and should be able to call geokit finders on Event! Excellent (so you can see above, I've added acts_as_mappable :through to the Event).

Here's my ActiveRecord query:

Event.find(:all, :origin => [lat, lng], :select => 'title', :within => 5, :limit => 10)

Here's the SQL generated:


SELECT `events`.`id` AS t0_r0, `events`.`title` AS t0_r1, `events`.`description` AS t0_r2,
 `events`.`created_at` AS t0_r3, `events`.`updated_at` AS t0_r4, `events`.`venue_id` AS 
t0_r5, `events`.`event_detail_id` AS t0_r6, `events`.`event_detail_type` AS t0_r7, 
`venues`.`id` AS t1_r0, `venues`.`name` AS t1_r1, `venues`.`lat` AS t1_r2, `venues`.`lng` 
AS t1_r3, `venues`.`created_at` AS t1_r4, `venues`.`updated_at` AS t1_r5 FROM `events` LEFT
 OUTER JOIN `venues` ON `venues`.id = `events`.venue_id WHERE 
(((venues.lat>51.4634898826039 AND venues.lat>51.5533406301823 AND venues.lng>-
0.197713629915149 AND venues.lng<-0.053351862714855)) AND 
((ACOS(least(1,COS(0.898991438708539)*COS(-0.00219095974226756)*COS(RADIANS(venues.lat))*COS(RADIANS(venues.lng))
 COS(0.898991438708539)*SIN(-
0.00219095974226756)*COS(RADIANS(venues.lat))*SIN(RADIANS(venues.lng))
 SIN(0.898991438708539)*SIN(RADIANS(venues.lat))))*6376.77271)
 <= 5)) LIMIT 10

Ouch. Well, this takes quite some time to run, and it's not just the math, I think there's a significant problem here with performance, this takes 2 seconds to run on my desktop! But getting down to it, here is my complaint:

  • To try and improve performance, you can see I have tried to use a SELECT statement in my ActiveRecord find query. I just want to know the TITLES of these Events that match, not all the other fields. But the query goes through, and SELECTs everything from Event! It uses strange aliases which I'm not familiar with like 't1_r1'. So this clearly is a problem.

Furthermore, this query, when run against the Venues alone (i.e. doing away with the JOIN) is executed in less than 10ms. So is there a way to do this Venues search first, and THEN do the join against the Events? I think the join must be being done on the two tables as a whole, and only then the geocoding part comes in and slims the dataset down.

Any help in tightening up this issue would be much appreciated (this is a non-commercial project). I feel that the model is very simple I should be able to do this without too much fuss?

Thanks in advance!

(Edit: In an early version of this post I was stupid, Limit worked fine, my view was broken, edited to take that part out.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

拍不死你 2024-08-15 19:42:05

It looks like acts_as_mappable is using the eager loading functionality of Rails Active Record. Once you start eager loading any :select argument is ignored and you cannot specify particular columns from a table/model. In your case i think this is happening because of the :through=>:venue argument to acts_as_mappable. Can you make it work without the through argument? You can always poke around the source code as well to see if there are any other adjustments you can make.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文