Вложенные SQL-запросы Rails
У меня есть модель базы данных Position(lat,lon)
, которая содержит latitudes
и longitudes.
У меня есть действие с контроллером show_close_by
, которое получает позицию в градусах (my_lat, my_lon)
, допустимость (в километрах) и должна возвращать список позиций в базе данных, находящихся в пределах допустимого диапазона.
Для этого я использую формулу haversine_distance, которая вычисляет расстояние в километрах (на поверхности Земли) между двумя координатами (lat1, lon1, lat2, lon2)
.
Чтобы сделать запрос быстрее, я написал целую формулу haversine_distance
в запросе:
... WHERE 2*6371*asin(sqrt( power( sin( (:lat2-latitude)*pi()/(180*2) ) ,2) + cos(latitude*pi()/180)*cos(:lat2*pi()/180)*power(sin( (:lon2-longitude)*pi()/(180*2) ),2) )) < tolerance
Специфика запроса не имеет значения. Я сомневаюсь: необходимо ли вычислить эту огромную функцию для КАЖДОЙ позиции в базе данных? Могу ли я отфильтровать некоторые позиции, которые явно слишком далеки, с более простой функцией?
Ну, я могу: с вложенным SQL-запросом я могу запросить базу данных для позиций, которые находятся в большом "квадрате" (в пространстве lat/lon), а затем отфильтровывать те, у которых более затратная тригонометрическая функция. Что-то вроде следующего:
SELECT * FROM ( SELECT * FROM Positions WHERE lat-latitude < some_reasonable_upper_bound AND lon-longitude < same_upper_bound ) WHERE costly_haversine_distance < tolerance
Наконец, мой вопрос: как я могу реализовать это в Rails (без написания всего запроса сам)? Выполняет ли Positions.where(reasonable_upper_bound).where(costly_but_accurate_restriction)
вложенный запрос? Если нет, то как?
Спасибо большое!
Ответы
Ответ 1
Здесь, как делать вложенные запросы:
LineItem.where(product_id: Product.where(price: 50))
Он выполняет следующий запрос:
SELECT "line_items".* FROM "line_items"
WHERE "line_items"."product_id" IN
(SELECT "products"."id" FROM "products" WHERE "products"."price" = 50)
Обратите внимание, что только id
будет извлечено из таблицы products
. Если вы пытаетесь сделать еще один способ подключения двух объектов, и эта магия не подходит, используйте Product.select(:some_field).where(...)
.
Ответ 2
Я хотел бы предложить обновленный ответ. В Rails v5.0.x вы можете использовать select
чтобы использовать атрибут, отличный от id, все еще выполняя вложенный запрос.
LineItem.where(product_id: Order.where(price: 50).select(:product_id))