【问题标题】:ActiveRecord Subquery Inner JoinActiveRecord 子查询内连接
【发布时间】:2015-01-16 22:44:51
【问题描述】:

我正在尝试将“原始”PostGIS SQL 查询转换为 Rails ActiveRecord 查询。我的目标是将两个连续的 ActiveRecord 查询(每个约 1 毫秒)转换为单个 ActiveRecord 查询(约 1 毫秒)。使用下面的 SQL 和 ActiveRecord::Base.connection.execute 我能够验证时间减少。

因此,我的直接请求是帮助我将此查询转换为 ActiveRecord 查询(以及执行它的最佳方式)。

SELECT COUNT(*)
FROM "users"
INNER JOIN (
  SELECT "centroid"
  FROM "zip_caches"
  WHERE "zip_caches"."postalcode" = '<postalcode>'
) AS "sub" ON ST_Intersects("users"."vendor_coverage", "sub"."centroid")
WHERE "users"."active" = 1;

注意值 &lt;postalcode&gt; 是此查询中唯一的变量数据。显然,这里有两个模型User 和ZipCache。 User 与 ZipCache 没有直接关系。

当前的两步 ActiveRecord 查询如下所示。

zip = ZipCache.select(:centroid).where(postalcode: '<postalcode>').limit(1).first
User.where{st_intersects(vendor_coverage, zip.centroid)}.count

【问题讨论】:

  • 我学到的最重要的技巧之一是,虽然你可以在 Ruby 中链接方法很好,但如果你在代码中链接,则表明你没有遵循Law Of Demeter。不要从查看 SQL 查询开始,您应该查看 ZipCache.select.where.limit.first 并了解如何通过向下移动逻辑来减少方法的数量。您从 ZipCache 而不是 User 模型开始处理查询,这有点奇怪......我错过了什么吗?
  • 我认为您“缺少”的部分是因为它是 ActiveRecord 链接是必需的。我敢于编写一个 ActiveRecord 查询,它只选择和填充一个模型和一个属性,并告诉我如何在没有方法链接的情况下执行此操作。有些东西告诉我你对 LoD 很迂腐。已经说过很多次了,但需要重复The Law of Demeter Is Not A Dot Counting Exercise。

标签: ruby activerecord postgis arel rgeo


【解决方案1】:

声明:我从未使用过 PostGIS

在您的最终请求中,您似乎错过了WHERE "users"."active" = 1; 部分。

这是我要做的:

首先在用户上添加一个active 范围(为了可重用性)

scope :active, -> { User.where(active: 1) }

那么对于实际的查询,你可以有子查询而不执行它,并在用户模型上的一个连接中使用它,例如:

subquery = ZipCache.select(:centroid).where(postalcode: '<postalcode>')
User.active
    .joins("INNER JOIN (#{subquery.to_sql}) sub ON ST_Intersects(users.vendor_coverage, sub.centroid)")
    .count

这允许最少的原始 SQL,同时只保留一个查询。

无论如何,通过将记录器级别设置为调试来检查控制台/日志中的实际 sql 请求。

【讨论】:

  • 您需要subquery.to_sql。只是 subquery 没有正确插值。
【解决方案2】:

神奇的工具scuttle.io 非常适合转换这些类型的查询:

User.select(Arel.star.count).where(User.arel_table[:active].eq(1)).joins(
  User.arel_table.join(ZipCach.arel_table).on(
    Arel::Nodes::NamedFunction.new(
      'ST_Intersects', [
        User.arel_table[:vendor_coverage], Sub.arel_table[:centroid]
      ]
    )
  ).join_sources
)

【讨论】:

  • 整洁,不知道天窗。这是正确的答案,但我建议在这种情况下只使用 SQL。如果你不做算法组合,那么用 ARel 编写它不会有任何收获。这几乎是不可穿透的。 .star.count!? 'ST_Intersects'!?为什么有一些 DSL 方法但 NamedFunction 不是?没有人应该真正编写此代码。
  • ... 并不是说​​性能在这里很重要,但是 ARel 至少有几十个方法调用,并且容易受到 API 更改的影响。你不需要用 SQL 来处理这个。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-13
  • 2019-08-04
  • 1970-01-01
相关资源
最近更新 更多