ActiveRecord子查询内部连接

Rya*_*yan 5 ruby activerecord postgis arel rgeo

我试图将"原始"PostGIS SQL查询转换为Rails ActiveRecord查询.我的目标是将两个连续的ActiveRecord查询(每个需要大约1毫秒)转换为单个ActiveRecord查询(~1ms).使用下面的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;
Run Code Online (Sandbox Code Playgroud)

请注意,该值<postalcode>是此查询中唯一的可变数据.显然,这里有两个型号UserZipCache.User与...没有直接关系ZipCache.

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

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

ast*_*eal 16

Disclamer:我从未使用过PostGIS

首先在你的最后请求中,似乎你错过了这个WHERE "users"."active" = 1;部分.

这是我要做的:

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

scope :active, -> { User.where(active: 1) }
Run Code Online (Sandbox Code Playgroud)

然后,对于实际查询,您可以在不执行子查询的情况下使用子查询,并在User模型的连接中使用它,例如:

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
Run Code Online (Sandbox Code Playgroud)

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

在任何情况下,通过将记录器级别设置为debug来检查控制台/日志中的实际sql请求.

  • 你需要`subquery.to_sql`.只是'子查询'没有正确插值. (7认同)