ActiveRecord
Pro ActiveRecord můžete do třídy Item umístit metodu jako je tato:
def self.with_all_categories(category_ids)
select(:id).distinct.
joins(:categories).
where('categories.id' => category_ids).
group(:id).
having('count(categories.id) = ?', category_ids.length)
end
Poté můžete filtrovat své dotazy takto:
category_ids = [1,2,3]
Item.where(id: Item.with_all_categories(category_ids))
Můžete také použít rozsahy, abyste to udělali trochu přátelštější:
class Item
scope :with_all_categories, ->(category_ids) { where(id: Item.ids_with_all_categories(category_ids)) }
def self.ids_with_all_categories(category_ids)
select(:id).distinct.
joins(:categories).
where('categories.id' => category_ids).
group(:id).
having('count(categories.id) = ?', category_ids.length)
end
end
Item.with_all_categories([1,2,3])
Oba vytvoří tento SQL
SELECT "items".*
FROM "items"
WHERE "items"."id" IN
(SELECT DISTINCT "items"."id"
FROM "items"
INNER JOIN "categories_items" ON "categories_items"."item_id" = "items"."id"
INNER JOIN "categories" ON "categories"."id" = "categories_items"."category_id"
WHERE "categories"."id" IN (1, 2, 3)
GROUP BY "items"."id"
HAVING count(categories.id) = 3)
Technicky nepotřebujete distinct
část tohoto dílčího dotazu, ale nejsem si jistý, zda by s nebo bez bylo pro výkon lepší.
SQL
V surovém SQL existuje několik přístupů
SELECT *
FROM items
WHERE items.id IN (
SELECT item_id
FROM categories_items
WHERE category_id IN (1,2,3)
GROUP BY item_id
HAVING COUNT(category_id) = 3
)
To bude fungovat v SQL Server - syntaxe se může v Postgresu mírně lišit. Nebo
SELECT *
FROM items
WHERE items.id IN (SELECT item_id FROM categories_items WHERE category_id = 1)
AND items.id IN (SELECT item_id FROM categories_items WHERE category_id = 2)
AND items.id IN (SELECT item_id FROM categories_items WHERE category_id = 3)