【问题标题】:Avoiding duplicate joins in Rails ActiveRecord query避免 Rails ActiveRecord 查询中的重复连接
【发布时间】:2013-04-02 16:33:29
【问题描述】:

我有一个场景,我在查询链中的某处有 SQL 连接,然后在更进一步的点我需要附加一个需要相同连接的条件,但我现在不知道该连接是否已经存在范围内。例如:

@foo = Foo.joins("INNER JOIN foos_bars ON foos_bars.foo_id = foos.id")
....
@foo.joins(:bars).where(bars: { id: 1 })

这将产生一个关于重复表/别名的 SQL 错误。

我在第一个实例中手动编写 SQL 连接的原因是为了提高效率,因为经典的 Rails AREL 连接会产生两个 INNER JOINS,而我只需要一个。

有没有推荐的解决方法?例如,某种检查当前范围内连接的方法。

对评论的回应:

使用has_and_belongs_to_many 关系Rails 产生两个INNER JOINS,如下所示:

SELECT "journals".* FROM "journals"
INNER JOIN "categories_journals"
  ON "categories_journals"."journal_id" = "journals"."id"
INNER JOIN "categories"
  ON "categories"."id" = "categories_journals"."category_id"
WHERE "categories"."id" = 1

而我相信我可以这样做:

SELECT "journals".* FROM "journals"
INNER JOIN "categories_journals"
  ON "categories_journals"."journal_id" = "journals"."id"
WHERE "categories_journals"."category_id" = 1

如果我错了,请纠正我。

【问题讨论】:

  • 你做了什么样的分析来证明你更“有效”的连接方式实际上更好?实际上,是什么让您认为您只需要一个加入?即使您一遍又一遍地尝试连接同一个表,AREL 也不会构建不必要的连接。
  • 我更新了我的问题,因为@messick 的评论太长了。
  • 我建议让 AREL 做这件事。您的解决方案正在破坏 ORM 的主要优势之一,即“自动”拥有映射到数据库表的对象。如果加入 Categories 表对性能真的有那么大的影响,我会回去考虑一下 Rails 是否真的是你想要使用的东西。
  • 我认为仅仅因为我选择在 SQL 中编写某些方面是出于性能原因而说我正在击败 ORM 的对象或使用 Rails 本身,这有点愚蠢。很多事情你在 AREL 中根本做不到,必须使用 SQL。我有一个包含大约 6 个多对多模型连接的查询,如果我可以将 SQL 连接的数量减少一半,为什么不呢?使用 Rails 并不是为了性能。

标签: ruby-on-rails


【解决方案1】:

解决方案是普遍使用字符串连接。我不知道 Rails 实际上是 uniqs 字符串连接——只要它们是字符串相同,这个问题就不会发生。

This article 引起了我的注意,作者表现出与我完全相同的问题并修补了 Rails,看起来补丁是很久以前实施的。我不认为它是完美的。 rails 应该有一种方法来处理散列参数连接和字符串连接,并且在它们重叠时不会爆炸。可能会看看我是否可以修补它..

编辑:

我做了几个基准测试,看看我是否真的什么都不担心(在两种加入方式之间):

1.9.3p194 :008 > time = Benchmark.realtime { 1000.times { a = Incident.joins("INNER JOIN categories_incidents ON categories_incidents.incident_id = incidents.id").where("categories_incidents.category_id = 1") } }
 => 0.042458 
1.9.3p194 :009 > time = Benchmark.realtime { 1000.times { a = Incident.joins(:categories).where(categories: { id: 1 }) } }
 => 0.152703

我不是常规的基准测试人员,因此我的基准测试可能并不完美,但在我看来,我的更有效的方法确实可以在大型查询或大量查询上提高现实世界的性能。

以我所做的方式加入的缺点是,如果 Category 不存在但仍记录在联接表中,那么这可能会导致一些问题,否则这些问题可以通过更彻底的联接来避免。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-21
    • 2012-08-03
    • 1970-01-01
    • 2013-06-30
    • 1970-01-01
    • 1970-01-01
    • 2019-04-16
    • 1970-01-01
    相关资源
    最近更新 更多