14

我有一个如下所示的交叉引用表:

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 中查询这个最简单和最有效的方法是什么?

4

4 回答 4

29

我假设这个表的自然键是document_id + subject_id,而那个id是代理项;IOW、document_id 和 subject_id 是唯一的。因此,我只是假装它不存在并且唯一的约束在自然键上。

让我们从显而易见的开始。

SELECT document_id, subject_id
  FROM document_subjects
 WHERE subject_id IN (17,76)

这会让你得到你想要的一切以及你不想要的东西。所以我们需要做的就是过滤掉其他的东西。“其他东西”是计数不等于所需主题计数的行组。

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)

显然,subjects_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))

管他呢。

于 2009-09-10T23:16:24.753 回答
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);
于 2015-06-09T20:04:31.347 回答
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。但我承认这不是一个很好的通用解决方案。

于 2009-09-10T23:04:25.093 回答
0
select document_id from table1
 where subject_id in (17, 76)
 group by document_id
having count(distinct subject_id) = 2
于 2009-09-10T23:15:48.873 回答