У меня есть модель, Prospect
.Я хочу отсортировать перспективы по некоторому значению (например, updated_at ASC
), однако я также хочу разделить отсортированный список пополам, чтобы сначала отображались все перспективы, в которых сначала отображается lead_id IS NOT NULL
, а затем updated_at ASC
, а затем все перспективы, в которых lead_id IS NULL
последний отсортированный по updated_at ASC
.Меня не волнует, что такое lead_id
, только то, что любые записи, где lead_id IS NOT NULL
расположены в начале списка.Мне нужно иметь возможность разбивать набор данных на страницы, и запрос должен быть относительно эффективным (время обработки должно быть не более 500 мс, в идеале не более 100 мс).
Первый подход
Сначала я попытался выполнить это с помощью следующего запроса, однако это не работает (для моего варианта использования), поскольку потенциальные клиенты сортируются по lead_id (как и следовало ожидать из этого запроса), который уникален, поэтому эффективно выполняет вторичную сортировкубесполезно.
Prospect.order("lead_id ASC nulls last, updated_at ASC")
Второй подход
Я попробовал (слегка измененный) подход, предложенный Андрей Дейнеко .Это возвращает весь набор данных в правильном порядке, но нет способа объединить два отдельных отношения в одно отношение, которое можно разбить на страницы.В результате, чтобы разбить набор данных на страницы, мне нужно создать экземпляр каждой строки таблицы в памяти.Это было бы приемлемо для нескольких десятков записей максимум, но, конечно, не 20k +.
# prospect.rb
scope :with_leads, -> { where.not(lead_id: nil) }
scope :without_leads, -> { where(lead_id: nil) }
scope :ordered, -> { order(:total_clicks_count) }
[Prospect.with_leads.ordered, Prospect.without_leads.ordered].flatten
Третий подход
Я понял, что могу получить отсортированный списокпроспект идентификаторы (как с, так и без lead_id) и использовать его, чтобы получить полный набор данных, упорядоченный по id.Это выполняет то, что мне нужно, и отлично работает для нескольких десятков или сотен записей, но не подходит для записей 20k +.
lead_ids = Prospect.where.not(lead_id: nil).pluck(:id)
prospect_ids = Prospect.where(lead_id: nil).pluck(:id)
prospects = Prospect.order_by_ids([lead_ids, prospect_ids].flatten)
Вот источник order_by_ids
:
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
def self.order_by_ids(ids)
# https://stackoverflow.com/a/26777669/4139179
order_by = ["CASE"]
ids.each_with_index do |id, index|
order_by << "WHEN #{self.name.underscore}s.id='#{id}' THEN #{index}"
end
order_by << "END"
order(order_by.join(" "))
end
end
Проблема с этим вторым подходом состоит в том, что для создания списка или упорядоченных идентификаторов для каждого подмножества (перспективы с и без lead_id) требуется до 1 секунды на 1000 записей, а затем используется для извлечения всего набора данных по порядку.
Есть ли лучший подход, который будет возвращать весь набор данных (таким образом, который может быть разбит на страницы), упорядоченный по некоторому атрибуту (например, updated_at ASC
), где перспективы с lead_id находятся вверху списка, а те, у которых нетвнизу?