Несмотря на то, что все это можно сделать в одном запросе, станет практически невозможно добиться от него хорошей производительности, когда таблицы станут большими (именно поэтому вы разбиваете на страницы, верно?), Потому что это действительно два разных типа запросы.
Live Query
В Rails эти два запроса будут:
products_without_one = Product.where.not('EXISTS (?)',
ProductDocument.where('products.id = product_documents.product_id').where(doc_type: 1).select(1)
)
products_with_one = Product.joins(:product_documents).where(product_documents: { doc_type: 1 }).distinct
Вы могли бы UNION
результаты вместе, но большую часть времени вы будете листать только один из двух запросов, прежде чем начнете листать другой.
Если вы действительно хотите выполнить это в одном запросе, или ваши таблицы не станут действительно большими, тогда выполните:
SELECT *
FROM (
SELECT DISTINCT ON(p.id) p.id, doc_type
FROM products p
LEFT JOIN product_documents pd ON pd.product_id = p.id
ORDER BY p.id ASC, doc_type DESC
) sub
ORDER BY doc_type
Столбец кеша
Если вам нужно выполнять эти запросы часто и быстро, я предлагаю изменить запрашиваемые данные. Добавление нового столбца в Product
, например:
class Product < ApplicationRecord
has_one :primary_doc_one, class_name: ProductDocument.to_s
end
# making the query:
Product.order_by('primary_doc_one_id NULLS FIRST') # or LAST
или
# with a Product#num_doc_type_one column
Product.order_by(:num_doc_type_one)
Хотя вам нужно убедиться, что вы правильно поддерживаете эти столбцы с транзакциями и т. Д. (Избегайте обратных вызовов если можете).
Mis c
Я пропустил .order('product_documents.doc_url')
, потому что это не имеет смысла. Если в Product
много ProductDocument
, какой doc_url
следует использовать при сортировке? например, дано:
product_documents
id | product_id | doc_url
-------------------------
1 | 1 | 'A'
2 | 1 | 'C'
3 | 2 | 'B'
(игнорируя doc_type
), если результат Product
будет:
{ product_id: 1, doc_url: 'A' }
{ product_id: 2, doc_url: 'B' }
или
{ product_id: 2, doc_url: 'B' }
{ product_id: 1, doc_url: 'C' }