Mysql SQL数据库中N对N关系的搜索

Mysql SQL数据库中N对N关系的搜索,mysql,Mysql,这是我的数据库模型: 我需要的是: 我需要输入几个术语并搜索包含所有这些术语的document.text文档 示例数据: 文件: SELECT document_iddocument, SUM(IF(term = 'cat', 1, 0)) AS has_0, SUM(IF(term = 'dog', 1, 0)) AS has_1 FROM document_has_term LEFT JOIN term ON (term_idterm = idterm AND

这是我的数据库模型:

我需要的是:

我需要输入几个术语并搜索包含所有这些术语的document.text文档

示例数据:

文件:

SELECT document_iddocument,
    SUM(IF(term = 'cat', 1, 0)) AS has_0,
    SUM(IF(term = 'dog', 1, 0)) AS has_1
    FROM document_has_term
    LEFT JOIN term ON (term_idterm = idterm AND term.term IN 
        ('cat', 'dog'))
GROUP BY document_iddocument;
条款:

例如:

我想搜索包含所有术语的文档:dog cat train。结果将是文件1和文件2,但不是文件3,因为它没有列车,而不是文件4,因为它没有cat或列车

我的第一次尝试是这样的查询:

select document.text from document join document_has_term on       
 document.iddocument=document_has_term.document_iddocument join term on
 term.idterm=document_has_term.term_idterm where term="kindness" and
 term="horrible"
SELECT document.* FROM document
WHERE iddocument IN (

SELECT document_iddocument
    FROM document_has_term
    LEFT JOIN term ON (term_idterm = idterm AND term.term IN 
    ('cat', 'dog') -- list of all terms used
    )
GROUP BY document_iddocument

    HAVING
    (SUM(IF(term = 'cat', 1, 0))!=0) -- for the term "CAT"
    AND NOT                          -- from the "textual query"
    (SUM(IF(term = 'dog', 1, 0))!=0) -- for the term "DOG"
);

此查询不选择任何帖子,但它反映了我基本上想要的内容。

按您要选择的列分组,只选择那些同时包含这两个术语的帖子

select document.text 
from document 
join document_has_term on document.iddocument=document_has_term.document_iddocument 
join term on term.idterm=document_has_term.term_idterm 
where term in ('kindness', 'horrible')
group by document.text 
having count(distinct term) = 2

假设每个文档只能有一个术语,如果您运行

SELECT document_iddocument
    FROM document_has_term
    JOIN term ON (term_idterm = idterm)
    WHERE term IN ('cat', 'dog', 'train');
您将有三行,其中三个术语中的每一个都匹配,两行 如果两个术语匹配,依此类推

所以

将仅输出具有三个匹配项的文档ID

此查询在此阶段甚至不需要访问文档

您可以将其用作子选择,以获取其iddocument在此ID列表中的文档:

SELECT document.text FROM document WHERE iddocument IN
( the above select );
更复杂的查询 如果您想运行更复杂的搜索,那么可能应该使用MySQL进行文本搜索并使用全文功能

否则,您需要从外部语言开始构建查询,在外部语言中指定

cat AND NOT dog
这不是SQL,并将其转换为SQL查询

一种有效的方法是尝试通过复杂的查询(比如cat而不是dog)来确定哪一个组件是最有限制的。在本例中,如果您有2000条记录,其中100条记录中有cat,50条记录中有dog,则需要考虑: -搜索是否存在术语的查询非常有效。 -搜索缺少术语的查询非常昂贵

您将首先运行cat查询,然后删除确实包含dog的项

这种方法也相当复杂

另一种可能性(对于大型数据库不太推荐)是扫描整个文档\u has\u term表并查找所有文档的状态:

SELECT document_iddocument,
    SUM(IF(term = 'cat', 1, 0)) AS has_0,
    SUM(IF(term = 'dog', 1, 0)) AS has_1
    FROM document_has_term
    LEFT JOIN term ON (term_idterm = idterm AND term.term IN 
        ('cat', 'dog'))
GROUP BY document_iddocument;
这个查询是用某种外部语言构建的,由三部分组成:模板

变成

(has_0) and not (has_1)
实际上,您可以将其集成到HAVING子句中,并按照如下方式构建查询:

select document.text from document join document_has_term on       
 document.iddocument=document_has_term.document_iddocument join term on
 term.idterm=document_has_term.term_idterm where term="kindness" and
 term="horrible"
SELECT document.* FROM document
WHERE iddocument IN (

SELECT document_iddocument
    FROM document_has_term
    LEFT JOIN term ON (term_idterm = idterm AND term.term IN 
    ('cat', 'dog') -- list of all terms used
    )
GROUP BY document_iddocument

    HAVING
    (SUM(IF(term = 'cat', 1, 0))!=0) -- for the term "CAT"
    AND NOT                          -- from the "textual query"
    (SUM(IF(term = 'dog', 1, 0))!=0) -- for the term "DOG"
);


只要在文本查询中使用SQLish语法,如果小心SQL注入。如果您不小心清理您的输入,只允许使用有效的术语和关键字“and”、“or”、“not”和括号,并使用准备好的查询?术语的占位符,很可能是您的…

这很有帮助,但是如果我不知道我要搜索多少术语呢?例如,“猫”、“狗”、“火车”中的术语是这样的子选择查询-其中“从术语中选择术语”中的术语是,其中术语=猫,而不是术语=狗这是。。。要复杂得多。为了学习,我尝试了一个简单的解决方案。这很有帮助,但是如果我不知道要搜索多少个术语呢?例如,“cat”、“dog”、“train”中的WHERE term是这样的子选择查询-SELECT term FROM term WHERE term=cat而非term=dog中的WHERE term您正在以某种方式构建查询,并且必须在那里填充WHERE子句。只需确定有多少个术语,并将该数字放入having子句中。
(has_0) and not (has_1)
SELECT document.* FROM document
WHERE iddocument IN (

SELECT document_iddocument
    FROM document_has_term
    LEFT JOIN term ON (term_idterm = idterm AND term.term IN 
    ('cat', 'dog') -- list of all terms used
    )
GROUP BY document_iddocument

    HAVING
    (SUM(IF(term = 'cat', 1, 0))!=0) -- for the term "CAT"
    AND NOT                          -- from the "textual query"
    (SUM(IF(term = 'dog', 1, 0))!=0) -- for the term "DOG"
);