我有这些表格: Products , Articles , Product_Articles
可以说, product_ids 是:p1,p2 article_ids 是:a1,a2,a3 product_articles 是:
- (p1,a1)
- (p1,a2)
- (p2,a1)
- (p2,a1)
- (p2,a2)
- (p2,a3)
如何查询product_id,它只有a1,a2,不多也不少?
更新尝试
SELECT p.*
FROM products p JOIN
(
SELECT product_id
FROM product_articles
GROUP BY product_id
HAVING COUNT(*) = SUM(CASE WHEN article_id IN (1, 2) THEN 1 ELSE 0 END)
AND SUM(CASE WHEN article_id IN (1, 2) THEN 1 ELSE 0 END) = 2
) q ON p.product_id = q.product_id
或者
SELECT p.*
FROM products p JOIN
(
SELECT product_id, COUNT(*) a_count
FROM product_articles
WHERE article_id IN (1, 2)
GROUP BY product_id
HAVING COUNT(*) = 2
) a ON p.product_id = a.product_id JOIN
(
SELECT product_id, COUNT(*) total_count
FROM product_articles
GROUP BY product_id
) b ON p.product_id = b.product_id
WHERE a.a_count = b.total_count
这是两个查询的SQLFiddle演示
这是“set-within-sets”子查询的一个示例。我提倡在逻辑上使用带有having
子句的聚合,因为这是表达关系的最通用方式。
这个想法是,您可以以类似于使用where
语句的方式计算产品(在这种情况下)中文章的出现次数。代码有点复杂,但它提供了灵活性。在您的情况下,这将是:
select pa.product_id
from product_articles pa
group by pa.product_id
having sum(case when pa.article_id = 'a1' then 1 else 0 end) > 0 and
sum(case when pa.article_id = 'a2' then 1 else 0 end) > 0 and
sum(case when pa.article_id not in ('a1', 'a2') then 1 else 0 end) = 0;
前两个子句计算两篇文章的出现次数,确保每篇文章至少出现一次。最后计算没有这两篇文章的行数,确保没有。
您可以看到这很容易推广到更多文章。或者查询你有“a1”和“a2”但没有“a3”的地方。或者您有四篇特定文章中的三篇,等等。
我相信这可以完全使用关系连接来完成,如下所示:
SELECT DISTINCT pa1.PRODUCT_ID
FROM PRODUCT_ARTICLES pa1
INNER JOIN PRODUCT_ARTICLES pa2
ON (pa2.PRODUCT_ID = pa1.PRODUCT_ID)
LEFT OUTER JOIN (SELECT *
FROM PRODUCT_ARTICLES
WHERE ARTICLE_ID NOT IN (1, 2)) pa3
ON (pa3.PRODUCT_ID = pa1.PRODUCT_ID)
WHERE pa1.ARTICLE_ID = 1 AND
pa2.ARTICLE_ID = 2 AND
pa3.PRODUCT_ID IS NULL
SQLFiddle在这里。
内连接查找与我们关心的文章相关的产品(文章 1 和 2 - 生成产品 1 和 2)。左外部查找与我们不关心的文章相关的产品(除了1 和 2 之外的任何文章),然后只接受没有任何不需要的文章的产品(即pa3.PRODUCT_ID IS NULL
,表示没有pa3
加入任何行)。