PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

So finden Sie Artikel mit *allen* übereinstimmenden Kategorien

ActiveRecord

Für ActiveRecord könnten Sie eine Methode wie diese in Ihre Item-Klasse einfügen:

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

Dann können Sie Ihre Abfragen wie folgt filtern:

category_ids = [1,2,3]
Item.where(id: Item.with_all_categories(category_ids))

Sie könnten auch Bereiche verwenden, um es ein wenig freundlicher zu gestalten:

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])

Beide erzeugen dieses 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)

Technisch gesehen brauchen Sie distinct nicht Teil dieser Unterabfrage, aber ich bin mir nicht sicher, ob mit oder ohne besser für die Leistung wäre.

SQL

Es gibt ein paar Ansätze in rohem SQL

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
)

Das funktioniert in SQL Server - die Syntax könnte in Postgres etwas anders sein. Oder

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)