过滤具有多对多关系的对象

时间:2009-12-09 00:03:48

标签: sql oracle many-to-many

我有一个两个表,一个用于映射到类别的文档。

Documents  
id | document_name  
1  | somename.doc  
2  | anothername.doc  

Documents_to_categories  
cat_id | doc_id  
10     | 1       
10     | 2  
11     | 3  
12     | 1

某些文档可以映射到多个类别。我想要的是能够选择属于多个类别的文档,如过滤方案。

基本上在脚本中我有一个文档ID数组,它是搜索结果。我需要根据类别过滤掉这些文档。

这就像我的目标一样(我知道它不起作用,但是例如)。

SELECT * 
 FROM Documents_to_categories A 
 JOIN Documents B ON A.doc_id = B.id 
WHERE B.id IN (6703,6614,2286) 
  AND A.cat_id = :ID0 
  AND A.cat_id = :ID1

编辑:对不起,我第一次发帖回复的所有人都不清楚问题。希望这更清楚我想要的东西。

5 个答案:

答案 0 :(得分:3)

select a.doc_id, count(*)
from Documents_to_categories A 
where a.doc_id in (<doc_id list>)
and a.cat_id in (<cat_id list>)
group by doc_id
having count(*) = <cat_id list length>

将返回一个doc_ids列表,其中包含cat_id list中每个类别的条目。请注意,您还需要提供having子句列表的长度。

您可以使用此选项检索外部选择中所需的所有详细信息,使用上面的选择来填充doc_ids的inlist。

这最终看起来像:

select b.id, b.document_name, a,cat_id
from Documents_to_categories A,
Documents B
where a.doc_id = b.id
and b.id in (select mylist.doc_id from (
    select a.doc_id, count(*)
    from Documents_to_categories A 
    where a.doc_id in (<doc_id list>)
    and a.cat_id in (<cat_id list>)
    group by doc_id
    having count(*) = <cat_id list length>

) as mylist )

答案 1 :(得分:1)

假设您将文档ID列表作为逗号分隔列表传递,最简单的解决方案是使用动态SQL:

L_CURSOR SYS_REFCURSOR;
L_QUERY  VARCHAR2(5000) DEFAULT 'SELECT d.document_name
                                   FROM DOCUMENTS d
                                   JOIN DOCUMENTS_TO_CATEGORIES c10 ON c10.doc_id = d.id
                                                                   AND c10.cat_id = 10
                                   JOIN DOCUMENTS_TO_CATEGORIES c12 ON c12.doc_id = d.id
                                                                   AND c12.cat_id = 12
                                  WHERE d.id IN (:document_list)'

BEGIN

  FOR I IN 0 .. (TRUNC(LENGTH(L_QUERY) / 255)) LOOP
    DBMS_OUTPUT.PUT_LINE(SUBSTR(L_QUERY, I * 255 + 1, 255));
  END LOOP;

  OPEN L_CURSOR FOR L_QUERY USING IN_DOCUMENT_LIST;
  RETURN L_CURSOR;

END;
  

我想要的是能够选择属于多个类别的文档,例如过滤方案。例如:我想按类别10和12进行筛选,因此只有属于10和12的文档才会返回(在本例中为doc 1)。

使用HAVING:

  SELECT d.document_name
    FROM DOCUMENTS d
    JOIN DOCUMENTS_TO_CATEGORIES dtc ON dtc.doc_id = d.id
   WHERE dtc.cat_id IN (10, 12)
GROUP BY d.document_name
  HAVING COUNT(DISTINCT dtc.cat_id) = 2

使用JOIN

SELECT d.document_name
  FROM DOCUMENTS d
  JOIN DOCUMENTS_TO_CATEGORIES c10 ON c10.doc_id = d.id
                                  AND c10.cat_id = 10
  JOIN DOCUMENTS_TO_CATEGORIES c12 ON c12.doc_id = d.id
                                  AND c12.cat_id = 12

答案 2 :(得分:0)

要获取仅出现在BOTH类别中的文档,可以使用ANSI SQL INTERSECT运算符,如下所示:

SELECT Documents.document_name  
FROM Documents 
WHERE Documents.id IN
(
    SELECT Documents_to_categories.docid 
    FROM Documents_to_categories  
    WHERE Documents_to_categories.catid = 10
  INTERSECT
    SELECT Documents_to_categories.docid 
    FROM Documents_to_categories  
    WHERE Documents_to_categories.catid = 12
/*.. add additional filters */
)

答案 3 :(得分:0)

  

我想按类别10过滤   12,所以只有文件属于10   12返回(在本例中为doc 1)

您需要从您的应用生成SQL查询:

SELECT
  d.id,
  d.document_name
FROM
  Documents as d,
  Documents_to_categories dc1,
  Documents_to_categories dc2,
WHERE
  d.id = dc1.doc_id
  AND d.id = dc2.doc_id
  AND dc2.cat_id = 10
  AND dc1.cat_id = 12

这适用于所有主要数据库服务器。

  

而不是从文件中选择   表,我想从中选择它   文档ID的数组。

不确定你的意思。关系世界中没有 array 实体(例如SQL)。有一个被称为表或可能设置。

答案 4 :(得分:0)

这为您提供属于至少一个类别的所有文档,其中至少有一个类别为10或12。

SELECT
  doc_id
FROM Documents_to_categories
WHERE doc_id IN (<your list of doc_ids>)
GROUP BY doc_id
HAVING COUNT(*) > 1
AND MAX(CASE WHEN cat_id IN (10, 12) THEN 1 ELSE 0 END) = 1;