2015-01-16 5 views
3

Я пытаюсь преобразовать «сырой» запрос PostGIS SQL в запрос Rails ActiveRecord. Моя цель состоит в том, чтобы преобразовать два последовательных запроса ActiveRecord (каждый из которых принимает ~ 1 мс) в один запрос ActiveRecord (~ 1 мс). Используя SQL ниже с ActiveRecord::Base.connection.execute, я смог проверить сокращение времени.ActiveRecord Subquery Inner Join

Таким образом, мой прямой запрос - помочь мне преобразовать этот запрос в запрос 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 
+0

Одним из наиболее важных советов, которые я узнал, является то, что, хотя приятно, что вы можете использовать методы в Ruby, если вы цепляетесь в свой код, это означает, что вы не следуете [Law Of Demeter] (http: /en.wikipedia.org/wiki/Law_of_Demeter). Не начинайте, глядя на ваш SQL-запрос, вы должны смотреть на свой ZipCache.select.where.limit.first и видеть, как вы можете уменьшить количество методов, перемещая логику вниз. Немного странно, что вы приближаетесь к запросу, начинающемуся с ZipCache, а не к модели User ... Я что-то упустил? – christoshrousis

+1

Я бы предположил, что часть, которую вы «пропустили», состоит в том, что требуется цепочки ActiveRecord. Я смею писать запрос ActiveRecord, который выбирает и заполняет только одну модель и только одно свойство и говорит мне, как это сделать без цепочки методов. Что-то говорит мне, что вы педантичны в LoD. Это было сказано много раз, но повторяется [Закон Деметры не является упражнением для подсчета точек] (http://haacked.com/archive/2009/07/14/law-of-demeter-dot-counting.aspx/). – Ryan

ответ

10

Юридическая информация: Я никогда не использовал 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}) sub ON ST_Intersects(users.vendor_coverage, sub.centroid)") 
    .count 

Это позволит минимальное необработанный SQL, сохраняя при этом только один запрос.

В любом случае, проверьте фактический запрос sql в консоли/журнале, установив уровень регистрации для отладки.

+1

Вам нужно 'subquery.to_sql'. Просто «подзапрос» не получается правильно интерполировать. – chadoh

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 
) 
+1

Ухоженный, не знал о скаттере. Это правильный ответ, но я бы рекомендовал в этом случае просто использовать SQL. Если вы не выполняете алгоритмическую композицию, вы ничего не получите, написав ее в ARel. Это очень непроницаемо. '.star.count' !? ' 'ST_Intersects'' !? И почему некоторые методы DSL, но «NamedFunction» нет? На самом деле никто не должен писать этот код. –

+0

...и не эта производительность имеет значение здесь, но AREL составляет не менее десятков вызовов методов и уязвим для изменений API. Вам не нужно иметь дело с этим с SQL. –