Внутреннее соединение подзапроса ActiveRecord

Я пытаюсь преобразовать «сырой» SQL-запрос PostGIS в запрос 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;

ПРИМЕЧАНИЕ, что значение <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

person Ryan    schedule 16.01.2015    source источник
comment
Один из самых важных советов, которые я усвоил, заключается в том, что, хотя и хорошо, что вы можете связывать методы в Ruby, если вы связываете их в своем коде, это указывает на то, что вы не следуете Закон Деметры. Не начинайте с просмотра вашего SQL-запроса, вы должны посмотреть на свой ZipCache.select.where.limit.first и посмотреть, как вы можете уменьшить количество методов, переместив логику вниз. Немного странно, что вы подходите к запросу, начинающемуся с ZipCache, а не с модели User... Я что-то упустил?   -  person Christos Hrousis    schedule 21.01.2015
comment
Я бы предположил, что часть, которую вам не хватает, заключается в том, что требуется цепочка ActiveRecord. Я предлагаю вам написать запрос ActiveRecord, который выбирает и заполняет только одну модель и только одно свойство, и скажите мне, как это сделать без цепочки методов. Что-то мне подсказывает, что ты педантичен в отношении LoD. Это было сказано много раз, но стоит повторить Закон Деметры — это не упражнение по подсчету точек.   -  person Ryan    schedule 21.01.2015


Ответы (2)


Отказ от ответственности: я никогда не использовал 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 в консоли/журнале, установив уровень ведения журнала на отладку.

person astreal    schedule 22.01.2015
comment
Вам нужно subquery.to_sql. Просто subquery неправильно интерполируется. - person chadoh; 06.12.2017

Удивительный инструмент 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
)
person Dan Kohn    schedule 21.01.2015
comment
Аккуратный, не знал про скаттл. Это правильный ответ, но я бы рекомендовал в этом случае просто использовать SQL. Если вы не занимаетесь алгоритмической композицией, вы ничего не получите, написав ее на ARel. Это почти непроницаемо. .star.count!? 'ST_Intersects'!? И почему некоторые методы DSL есть, а NamedFunction нет? На самом деле никто не должен писать этот код. - person Xavier Shay; 21.01.2015
comment
... и не то, чтобы производительность здесь имела значение, но ARel имеет как минимум десятки вызовов методов и уязвим для изменений API. Вам не нужно иметь дело с этим с SQL. - person Xavier Shay; 21.01.2015