【问题标题】:How should I use Rails to index and query a join table?我应该如何使用 Rails 来索引和查询连接表?
【发布时间】:2015-11-26 19:48:12
【问题描述】:

我有一个 ruby​​ on Rails 4 应用程序,使用 devise 以及 User 模型和 Deal 模型。

我正在为 User 和 Deal 之间的 has_many/has_many 关系创建一个 user_deals 表。

这里是迁移

class CreateUserDeals < ActiveRecord::Migration

  def change
    create_table :user_deals do |t|
        t.belongs_to :user
      t.belongs_to :deal
      t.integer         :nb_views

      t.timestamps
    end
  end
end

当用户加载一个 Deal(例如 Deal id=4)时,我使用了一个名为show的方法

controllers/deal.rb
#for the view of the Deal page
def show
end

在此 Deal id=4 页面的视图中,我需要在用户当前所在的 Deal 页面内显示 Devise 的current_user 的浏览次数

deal/show.html

here is the nb of views of user: <% current_user.#{deal_id}.nb_views%>

假设我有 10M+ user_deals 行,我想知道是否应该使用索引

add_index :user_deals, :user_id
add_index :user_deals, :deal_id

或许

add_index(:deals, [:user_id, deal_id])

确实在其他情况下我会说是的,但在这里我不知道 Rails 是如何在幕后工作的。感觉好像 Rails 知道在我不需要加快进程的情况下该怎么做,......好像当 Rails 加载此视图时没有 SQL 查询(例如'找到视图的 nb WHERe user_id= xdeal_id= Y')....因为我只用于登录的current_user(通过设计的current_user)和deal_id Rails 知道这一点,因为我们在这笔交易的页面上(显示页面)所以我只是将它作为参数传递。

那么我是否需要一个索引来加速它?

【问题讨论】:

  • Rails 对索引本身没有任何作用。数据库管理索引,rails 仅代表创建它们。所以这取决于你的数据库。我建议您为外键创建索引,因为它们经常连接到其他表。在我看来,您应该始终为外键创建索引。对于多列索引,只有当我有 many to many 关联或者我必须检查特定范围内的唯一性时,我才会使用索引。

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord devise


【解决方案1】:

您关于索引的问题是一个很好的问题。 Rails确实生成 SQL* 来发挥它的魔力,因此适用于优化数据库的常规规则。

设计的魔力只延伸到 current_user。它使用高效的 SQL 查询来获取他们的详细信息,因为 devise 创建的用户表在默认情况下具有有用的索引。但这些不是您需要的索引。

首先,有一种更简洁、更惯用的方式来做你想做的事情

class CreateUserDeals < ActiveRecord::Migration
  def change
    create_join_table :users, :deals do |t|
      t.integer :nb_views
      t.index [:user_id, :deal_id]
      t.index [:deal_id, :user_id]
      t.timestamps
    end
  end
end

您会注意到迁移包含两个索引。如果您从未期望为给定交易创建所有用户的视图,那么您将不需要这些索引中的第二个。然而,正如@chiptuned 所说,索引每个外键几乎总是正确的调用。整数索引花费很少的写入资源,但在读取方面节省了大量资金。这是一个成本非常低的默认防御位置。

如果您将数据获取逻辑放在控制器中,您将拥有更好的时间,并且事情会变得更清晰。此外,您正在展示一个交易,因此将它而不是current_user 作为数据获取的中心会感觉不错。

您实际上可以在不使用through 关联的情况下执行此查询,因为您可以在不触及用户表的情况下执行此操作。 (不过,在其他情况下,您可能需要 through 关联。) 只需 has_many :user_deals 就可以完成这项工作。

为了最好地利用数据库引擎并在一个查询中执行此操作,您的控制器可以如下所示:

def show
  @deal = Deal.includes(:user_deals)
              .joins(:user_deals)
              .where("user_deals.user_id = ?", current_user.id)
              .find(params["deal_id"])
end

那么在你看来……

I can get info about the deal: <%= @deal.description %>

感谢includes,我无需单独的 SQL 查询即可获取用户 nb_views:

* 如果您想查看 SQL rails 神奇地生成了什么,只需将.to_sql 放在末尾即可。例如sql_string = current_user.deals.to_sql@deal.to_sql

【讨论】:

    【解决方案2】:

    是的,您应该使用索引来加快user_deals 记录的查询速度。至少在user_id 上肯定是这样,但正如你所说,可能在[:user_id, :deal_id] 上都有。

    至于为什么看不到 SQL 查询...

    首先,您在视图中的代码似乎不正确。假设您在 User 类上设置了 has_many :deals, through: :user_deals 关联,它应该类似于:

    here is the nb of views of user: &lt;%= current_user.deals.find(deal_id).nb_views %&gt;

    如果您看到nb_views 显示了正确的数字,则应在呈现视图时进行查询,除非current_user.deals 在处理过程的早期已经被加载,或者您正在进行某种缓存.

    如果 Rails 有“意识”,那么你应该弄清楚它背后的某种原因。预期的基本 Rails 行为是在那里发出 SQL 查询。

    【讨论】:

      【解决方案3】:

      不是索引其他表的一种更简洁的方法:

      class CreateUserDeals < ActiveRecord::Migration
      
         def change
            create_table :user_deals do |t|
               t.references :user
               t.references :deal
               t.integer :nb_views
      
               t.timestamps
            end
         end
      end
      

      【讨论】:

      • 我相信belongs_toreferences是同义词
      • @Adamantish references 还为 Rails 4 中的列添加了索引,这就是它的不同之处。如果作者使用多列索引,那么他们会想要使用比这个建议更接近他们迁移的东西。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-12-20
      • 2014-03-01
      • 2018-05-22
      • 1970-01-01
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多