我有一个交叉引用表,如下所示:
id document_id subject_id
1 8 21
2 5 17
3 5 76
4 7 88
5 9 17
6 9 76
7 2 76
它将文档与主题相匹配。文件可以是多个主题的成员。我想返回此表中的行,其中给定文档与给定集合中的主题全部匹配。例如,给定一组主题:
(17,76)
我想只返回与交叉引用表中某处的所有主题(至少)匹配的文档的行。给定上述设置的期望输出集将是:
id document_id subject_id
2 5 17
3 5 76
5 9 17
6 9 76
请注意,不会返回表格的最后一行,因为该文档仅匹配其中一个必需主题。
在SQL中查询此内容的最简单,最有效的方法是什么?
答案 0 :(得分:28)
我假设这个表的自然关键是document_id + subject_id,那个id是代理; IOW,document_id和subject_id是唯一的。因此,我只是假装它不存在,并且在自然键上有一个独特的约束。
让我们从明显的开始。
SELECT document_id, subject_id
FROM document_subjects
WHERE subject_id IN (17,76)
它可以为您提供所需的一切 plus 您不想要的东西。所以我们需要做的就是过滤掉其他东西。 “其他东西”是一组行,其计数不等于所需科目的计数。
SELECT document_id
FROM document_subjects
WHERE subject_id IN (17,76)
GROUP BY document_id
HAVING COUNT(*) = 2
请注意,subject_id已删除,因为它不参与分组。更进一步,我将添加一个名为subjects_i_want的虚构表,其中包含您想要的N行主题。
SELECT document_id
FROM document_subjects
WHERE subject_id IN (SELECT subject_id FROM subjects_i_want)
GROUP BY document_id
HAVING COUNT(*) = (SELECT COUNT(*) FROM subjects_i_want)
显然,subject_i_want可以换成另一个子查询,临时表或其他任何东西。但是,一旦有了document_id列表,就可以在更大查询的子选择中使用它。
SELECT document_id, subject_id, ...
FROM document_subjects
WHERE document_id IN(
SELECT document_id
FROM document_subjects
WHERE subject_id IN (SELECT subject_id FROM subjects_i_want)
GROUP BY document_id
HAVING COUNT(*) = (SELECT COUNT(*) FROM subjects_i_want))
或者其他什么。
答案 1 :(得分:2)
使用Oracle(或任何允许使用with子句的数据库)。这允许只定义一次subject_id值。
with t as (select distinct document_id from table1 where subject_id in (17,76) )
select document_id from table1 where subject_id in (select subject_id from t)
group by document_id
having count(*) = (select count (*) from t);
答案 2 :(得分:1)
这是一个非常有趣的问题。
我假设你想要一个更通用的查询,但这就是我总是拥有相同数量的主题(比方说两个)时会做的事情:
SELECT T.id, T.document_id, T.subject_id
FROM table T
INNER JOIN table T1 ON T.document_id = T1.document_id AND T1.subject_ID = 17
INNER JOIN table T2 ON T.document_id = T2.document_id AND T2.subject_ID = 76
当然,您可以添加另一个INNER JOIN来添加另一个主题ID ..但我承认这不是一个非常好的通用解决方案。
答案 3 :(得分:0)
select document_id from table1
where subject_id in (17, 76)
group by document_id
having count(distinct subject_id) = 2