ActiveRecord 子查询内部连接
ActiveRecord Subquery Inner Join
我正在尝试将 "raw" PostGIS SQL 查询转换为 Rails ActiveRecord 查询。我的目标是将两个连续的 ActiveRecord 查询(每个耗时约 1ms)转换为单个 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;
注意 值 <postalcode>
是此查询中唯一的可变数据。显然,这里有两个模型User
和ZipCache
。 User
与 ZipCache
没有直接关系。
当前的两步 ActiveRecord 查询如下所示。
zip = ZipCache.select(:centroid).where(postalcode: '<postalcode>').limit(1).first
User.where{st_intersects(vendor_coverage, zip.centroid)}.count
神奇的工具 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
)
免责声明:我从未使用过 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,同时只保留一个查询。
无论如何,请通过将记录器级别设置为调试来检查 console/log 中实际的 sql 请求。
我正在尝试将 "raw" PostGIS SQL 查询转换为 Rails ActiveRecord 查询。我的目标是将两个连续的 ActiveRecord 查询(每个耗时约 1ms)转换为单个 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;
注意 值 <postalcode>
是此查询中唯一的可变数据。显然,这里有两个模型User
和ZipCache
。 User
与 ZipCache
没有直接关系。
当前的两步 ActiveRecord 查询如下所示。
zip = ZipCache.select(:centroid).where(postalcode: '<postalcode>').limit(1).first
User.where{st_intersects(vendor_coverage, zip.centroid)}.count
神奇的工具 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
)
免责声明:我从未使用过 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,同时只保留一个查询。
无论如何,请通过将记录器级别设置为调试来检查 console/log 中实际的 sql 请求。