这可以重构为单个数据库调用吗?

发布于 2024-12-01 00:14:34 字数 822 浏览 1 评论 0原文

我在 Events 模型中有一个事件列表。我需要做的是按城市/州对事件进行可视化分组。

从视觉上看,它看起来像这样:

Tracking details

这是我认为我正在做的事情的精简外观实现这一点:

<% @number.events.locations.each do |location| %>
  <li>
    <h4><%= "#{location.city}, #{location.state}" %></h4>
    <ol>
    <% @number.events.city_state(location).each do |event| %>
      <li><%= event.status %></li>
    <% end %>
    </ol>
  </li>
<% end %>

以及相应的范围:

scope :locations, :group => 'city, state'
scope :city_state, lambda {|location| {:conditions => ["city = ? AND state = ?", location.city, location.state] }}

但问题是我正在为每个位置进行额外的数据库调用。

那么我可以将其简化为单个查询吗?

I have a listing of events in an Events model. What I need to do is visually group events by city/state.

Visually, that will look like this:

Tracking details

Here's, a stripped down look of what I'm doing in my view to make this happen:

<% @number.events.locations.each do |location| %>
  <li>
    <h4><%= "#{location.city}, #{location.state}" %></h4>
    <ol>
    <% @number.events.city_state(location).each do |event| %>
      <li><%= event.status %></li>
    <% end %>
    </ol>
  </li>
<% end %>

And the corresponding scopes:

scope :locations, :group => 'city, state'
scope :city_state, lambda {|location| {:conditions => ["city = ? AND state = ?", location.city, location.state] }}

The problem, though, is that I'm doing an additional database call for every location.

So can I get that down to a single query?

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

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

发布评论

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

评论(2

泅渡 2024-12-08 00:14:34

使用预先加载从数据库中预取关联:

如果您一次提取所有事件,您可以将其更改为预加载事件的位置关联:

Event.all      # only loads Event objects from the database

#maybe try...

Event.includes(:locations)    # loads Event objects and their Locations

如果您需要某些其他对象的事件(例如您的@number 对象),您可能希望在控制器中执行类似的操作,例如:

@number = Number.where(:id => whatever).includes(:events => :locations)

现在,在此示例中,将从数据库中提取您的 @number 对象以及所有的事件和所有这些事件的地点。

有关更多信息,请参阅 Rails 指南:http://guides.rubyonrails .org/active_record_querying.html#eager-loading-associations

更新了我在评论中的答案

根本不使用范围,您可以简单地使用 Ruby 为您完成这项工作,并将 ActiveRecord 和额外的数据库调用从循环中剔除。

Enumerable#group_by 将根据您提供的某些属性获取集合和分组。在这种情况下,您可以获取所有事件,按城市和州进行分组,然后迭代它返回的哈希值:

events = Event.all
events.group_by { |e| "#{e.city}, #{e.state}" }
# => {"Birmingham, AL" => [<event>, <event>, <event>], "Nashville, TN" => [<event>, <event>]}

这样做可能比使用范围不太漂亮,但它允许您进行一次数据库调用,并且就这样吧。

Use eager loading to pre-fetch associations from your database:

If you're pulling all of your events at once, you could change it to pre-load the location associations for the events:

Event.all      # only loads Event objects from the database

#maybe try...

Event.includes(:locations)    # loads Event objects and their Locations

If you need events for some other object (like your @number object), you might want to do something like this in your controller, for example:

@number = Number.where(:id => whatever).includes(:events => :locations)

Now, in this example, your @number object will be pulled from the database along with all of it's events and all of those events' locations.

See the Rails guides for a little more info on this: http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations

Updated with my answer from the comments:

Instead of using scopes, at all, you can simply use Ruby to do the work for you and cut ActiveRecord and extra database calls out of the loop.

Enumerable#group_by will take a collection and group by some attribute you provide. In this case, you could take all of your Events, group by the city and state, and then iterate over the hash it returns:

events = Event.all
events.group_by { |e| "#{e.city}, #{e.state}" }
# => {"Birmingham, AL" => [<event>, <event>, <event>], "Nashville, TN" => [<event>, <event>]}

Doing this may be a little less pretty than using scopes, but it allows you to make one database call and leave it at that.

奶气 2024-12-08 00:14:34

这是一个 O(n^2) 解决方案,假设您的 locations.each do |location| 循环按广告所示工作。应该将其降低至 2 分贝。

<% @number.events.locations.each do |location| %>
  <li>
    <h4><%= "#{location.city}, #{location.state}" -%></h4>
    <ol>
      <% @number.events.each do |event| %>
        <% if event.city == location.city and event.state == location.state %>
        <li><%= event.status -%></li>
        <% end %>
      <% end %>
    </ol>
  </li>
<% end %>

尚未测试,稍后再回来查看。

Here's an O(n^2) solution, assuming your locations.each do |location| loop works as advertised. Should cut it down to 2 db hits.

<% @number.events.locations.each do |location| %>
  <li>
    <h4><%= "#{location.city}, #{location.state}" -%></h4>
    <ol>
      <% @number.events.each do |event| %>
        <% if event.city == location.city and event.state == location.state %>
        <li><%= event.status -%></li>
        <% end %>
      <% end %>
    </ol>
  </li>
<% end %>

Not tested yet, will check back later.

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