Что было бы оптимизированным (быстрым) эквивалентом ActiveRecord в Ruby:
SELECT DISTINCT customers.city
FROM customers
INNER JOIN (
SELECT c_id
FROM carts
WHERE shop_id = #{`shop_id`}
ORDER BY created_at DESC
LIMIT 1000 OFFSET 1000
) AS filtered_carts ON customers.id = filtered_carts.c_id;
Примечание. shop_id
— это рубиновая переменная, передаваемая в запрос/ассоциацию.
Спасибо
Этот приведенный выше SQL работает так быстро, как ожидалось.
Я пытаюсь найти чистое решение ActiveRecord, чтобы избежать чего-то подобного:
customers = Customer
.select(:city)
.distinct
.joins("
INNER JOIN (
SELECT c_id
FROM carts
WHERE shop_id = #{`shop_id`}
ORDER BY created_at DESC
LIMIT 1000 OFFSET 1000
) AS filtered_carts
ON customers.id = filtered_carts.c_id
")
Причина в том, чтобы избежать жестко запрограммированного SQL в ActiveRecord, чтобы предотвратить возможные будущие проблемы с обратной совместимостью возможных изменений и миграций модели...
Было просто произвольно демонстрировать попытку разделить работу на множество частей (offset
и limit
). Я пытаюсь найти решение как для тысячи, так и для миллиона записей.
Я думаю, что вам вообще не нужен JOIN
. Подзапроса в where
должно быть достаточно:
customer_ids =
Cart
.select(:c_id)
.where(shop_id: shop_id)
.order(:created_at)
.limit(1000)
.offset(1000)
Customer
.where(id: customer_ids)
.distinct
.pluck(:city)
Обратите внимание, что customer_ids
— это всего лишь ActiveRecord::Relation
и что запрос не выполняется немедленно. Он запускается как подзапрос только при выполнении второго запроса.
Спасибо - один вопрос - если у вас более 1 миллиона записей о клиентах, как это повлияет на производительность Ruby/ActiveRecord (передача хэша, строки или массива с таким количеством записей)? Это подразумевает удаление offset
и limit
из запроса.
Это зависит от многих факторов: спецификации сервера базы данных, насколько хорошо установлены индексы в таблицах базы данных, памяти вашего сервера приложений, насколько велики записи. Поэтому я не могу дать хороший ответ на этот вопрос. Вы просто должны попробовать это. В общем, подзапрос, вероятно, более эффективен, чем объединение, но чем больше записей соответствует запросу, тем медленнее будет ответ, тем больше памяти потребуется серверам для выполнения запроса.
@spickermann будет ли WHERE EXIST(...)
быстрее, чем WHERE "customers"."id" IN(...)
?
@max Просто быстро изучил эту тему. И похоже, что это зависит от размера подзапроса и от конкретной реализации в базе данных.
Зачем ты это делаешь
OFFSET 1000
?