2

これが私のデータの簡略化されたバージョンです:

products:
+----+-----------+
| id | name      |
+----+-----------+
|  1 | Product X |
|  2 | Product Y |
|  3 | Product Z |
+----+-----------+

categories:
+----+---------------+
| id | name          |
+----+---------------+
|  1 | Hotel         |
|  2 | Accommodation |
+----+---------------+

category_product
+----+------------+-------------+
| id | product_id | category_id |
+----+------------+-------------+
|  1 |          1 |           1 |
|  2 |          1 |           2 |
|  3 |          2 |           1 |
|  4 |          3 |           2 |
+----+------------+-------------+

「ホテル」と「宿泊施設」の両方のproductsカテゴリ (例: 製品 X) のみを取得する効率的なクエリを作成するにはどうすればよいですか?

私は最初に参加アプローチを試みました

SELECT *
FROM products p
JOIN category_product cp
ON p.id = cp.product_id
WHERE cp.category_id = 1 OR cp.category_id = 2

^両方を含むようにクエリを制限しないため、これは機能しません。

機能するサブクエリを使用するアプローチを見つけました...しかし、パフォーマンス上の理由からサブクエリに対して警告されました:

SELECT *
FROM products p
WHERE
(
    SELECT id
    FROM category_product
    WHERE product_id = p.id
    AND category_id = 1
)
AND
(
    SELECT id
    FROM category_product
    WHERE product_id = p.id
    AND category_id = 2
)

より良い解決策はありますか (または代替案はありますか)? カテゴリを製品の追加の列に非正規化することを検討しましたが、理想的にはそれを避けたいと思います. 魔法の弾丸の解決を願って!

アップデート

回答で提供されている(優れた)ソリューションのいくつかを実行しました:私のデータは235 000のcategory_product行と58 000の製品であり、明らかにベンチマークは常に環境やインデックスなどに依存しています.

「関係分割」 @podiluska

2 categories: 2826 rows  ~ 20ms 
5 categories: 46 rows ~ 25-30 ms 
8 categories: 1 rows ~ 25-30 ms 

「存在する場所」@Tim Schmelter

2 categories: 2826 rows  ~ 5-7ms 
5 categories: 46 rows ~ 30 ms 
8 categories: 1 rows ~ 300 ms 

より多くのカテゴリが投入されると、結果が発散し始めることがわかります。一貫した結果が得られるため、「関係分割」の使用を検討しますが、実装により、「存在する場所」も確認する可能性があります (長い形式のhttp ://pastebin.com/6NRX0QbJ )

4

5 に答える 5

4
SELECT p.*
FROM products p
     inner join 
(
    select product_ID
    from category_product
    where category_id in (1,2)
    group by product_id
    having count(distinct category_id)=2
) pc
    on p.id = pc.product_id

この手法は「関係分割」と呼ばれます

于 2012-10-26T08:50:48.627 に答える
0
SELECT categories.name,products.name 
FROM 
category_product,category,product 
where 
    category_product.product_id=product.id 
and 
   category_product.category_id=category.id 
    and 
   (
      select count(1) from category_product 
      where 
      category_product.categoty_id=1
      or 
      category_product.categoty_id=2 
     group by product_id having count(1)=2
   )
于 2012-10-26T09:09:30.443 に答える
0
select *
from products p
where
    (
        select
            count(distinct cp.category_id)
        from category_product as cp
        where
            cp.product_id = p.id and
            cp.category_id in (1, 2)
    ) = 2

または、exists を使用できます

select *
from products p
where
    exists
    (
        select
            count(distinct cp.category_id)
        from category_product as cp
        where
            cp.product_id = p.id and
            cp.category_id in (1, 2)
        having count(distinct cp.category_id) = 2
    )
于 2012-10-26T08:50:57.203 に答える
0

私は使用しますEXISTS

SELECT P.* FROM Products P
WHERE EXISTS
(
    SELECT 1 FROM category_product cp
    WHERE cp.product_id = p.id
    AND category_id = 1
)
AND EXISTS
(
    SELECT 1 FROM category_product cp
    WHERE cp.product_id = p.id
    AND category_id = 2
)
于 2012-10-26T08:51:51.163 に答える
-1
SELECT p.id
FROM products p
JOIN category_product cp
ON p.id = cp.product_id
WHERE cp.category_id IN (1,2)
GROUP BY p.id
HAVING COUNT(DISTINCT cp.category_id) = 2
于 2012-10-26T08:53:41.937 に答える