ActiveRecord
Untuk ActiveRecord, Anda bisa meletakkan metode seperti ini di kelas Item Anda:
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
Kemudian Anda dapat memfilter kueri Anda seperti ini:
category_ids = [1,2,3]
Item.where(id: Item.with_all_categories(category_ids))
Anda juga dapat menggunakan cakupan untuk membuatnya sedikit lebih ramah:
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])
Keduanya akan menghasilkan SQL ini
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)
Secara teknis Anda tidak memerlukan distinct
bagian dari subkueri itu, tetapi saya tidak yakin apakah dengan atau tanpa itu kinerjanya akan lebih baik.
SQL
Ada beberapa pendekatan dalam SQL mentah
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
)
Itu akan berfungsi di SQL Server - sintaksnya mungkin sedikit berbeda di Postgres. Atau
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)