简体   繁体   English

MySQL连接三个表并搜索在一个表中找到的几个关键字

[英]mySQL join three tables and search on several keywords found in one table

I'm trying to limit the size of my database by avoiding multiple records containing the same words and therefore I have build two tables used to relate keyword to rows in other tables. 我试图通过避免包含相同单词的多个记录来限制数据库的大小,因此我建立了两个表,用于将关键字与其他表中的行相关联。

As an example, we use the follwing search input: "find my" 例如,我们使用以下搜索输入:“找到我”

What is wish go get as a hit, is the product_name from product_id = 106. Could I just search on the product name? 希望得到什么,这是product_id = 106中的product_name。我可以仅搜索产品名称吗? No, because the product can be associated with other keywords, that are not part of the product_name. 否,因为产品可以与其他关键字(不属于product_name的一部分)相关联。

TABLE: ec_products 表格:ec_products

product_id    | product_name    | is_deleted
---------------------------------------------
106           | some product    | 0

TABLE: ec_keywords_index 表格:ec_keywords_index

word_id       | keyword
---------------------------------------------
55            | find
61            | my
77            | product

TABLE: ec_keyword_relations 表格:ec_keyword_relations

word_id       | application_id    | related_id
------------------------------------------------
55            | 1                 | 106
61            | 1                 | 106
77            | 1                 | 106

So, what I want now when I enter "find my" into my search input is: 因此,当我在搜索输入中输入“ find my”时,我现在想要的是:

  • I only want hits where ec_keyword_relations.application_id = 1 我只想要ec_keyword_relations.application_id = 1的匹配
  • All words in my search string should exist in relation to the product. 我的搜索字符串中的所有单词都应与产品有关。 Eg if i change the search input to "find my pants", we SHOULD NOT find product_id = 106. 例如,如果我将搜索输入更改为“查找裤子”,则不应找到product_id = 106。

Here is what I've tried so far (and I know that I am very far from a working solution at the time being): 这是到目前为止我已经尝试过的方法(并且我知道我暂时还没有一个可行的解决方案):

    $searchInput = 'find my';
    $keywords = explode(' ', $searchInput);

    $keyword_count = count($keywords);

    $n = 1;
    $query = '';

    foreach($keywords as $keyword) { 
        if ($n !== $keyword_count) { 
            $query_ext = ' AND ';
        } else { 
            $query_ext = ''; 
        } 

        $query .= "(ec_keywords_index.keyword LIKE '%$keyword%')" . $query_ext;

        ++$n;
    }

    $query = "
        SELECT 
            ec_products.product_name

        FROM 
            ec_products

        LEFT JOIN ec_search_word_relations
            ON ec_keyword_relations.related_id = ec_products.product_id

        LEFT JOIN ec_keywords_index
            ON ec_keywords_index.word_id = ec_keyword_relations.word_id

        WHERE 
            ($query) 
        AND 
            ec_products.is_deleted = '0' 
        AND 
            ec_keyword_relations.application_id = '1'

        GROUP BY
            ec_products.product_id

        ORDER BY 
            ec_products.product_name ASC 

        LIMIT 
            100";

    $stmt = $mysqli->prepare($query);
    $stmt->execute();
    $stmt->store_result();
    $stmt->bind_result($name);
    while ($stmt->fetch()) {
        echo $name;
    }

UPDATE: 更新:

After some fiddling around, I have come up with this solution which works just as expected. 经过一番摆弄之后,我想出了可以按预期工作的解决方案。 Is it (in terms) of performance a good or a bad solution? (就性能而言)是好的还是坏的解决方案?

    $searchInput = 'find my';
    $keywords = explode(' ', $searchInput);

    $keyword_count = count($keywords);

    $n = 1;
    $where_query = '';
    $having_query = '';

    foreach($keywords as $keyword) { 
        if ($n != $keyword_count) {
            $w_query_ext = ' OR '; 
            $h_query_ext = ' AND ';
        } else { 
            $w_query_ext = ''; 
            $h_query_ext = ''; 
        }

        $where_query .= "ec_keywords_index.keyword LIKE '%" . $keyword . "%'" . $w_query_ext;

        $having_query .= "related_keywords LIKE '%" . $keyword . "%'" . $h_query_ext;

        ++$n;
    }

    $query = "
        SELECT 
            ec_products.product_name, 
            GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'

        FROM 
            ec_products, 
            ec_keywords_index

        LEFT JOIN 
            ec_keyword_relations 
            ON ec_keyword_relations.word_id = ec_keywords_index.word_id

        WHERE ($where_query) 
            AND ec_products.product_id = ec_keyword_relations.related_id 
            AND ec_keyword_relations.application_id = '1' 
            AND ec_products.is_deleted = '0'

        GROUP BY 
            ec_products.product_name

        HAVING 
            ($having_query)

        LIMIT 
            100";

    $stmt = $mysqli->prepare($query);
    $stmt->execute();
    $stmt->store_result();
    $stmt->bind_result($name);
    while ($stmt->fetch()) {
        echo $name;
    }

The above example outputs a query like this: 上面的示例输出如下查询:

    SELECT 
        ec_products.product_name, 
        GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'

    FROM 
        ec_products, 
        ec_keywords_index

    LEFT JOIN 
        ec_keyword_relations 
        ON ec_keyword_relations.word_id = ec_keywords_index.word_id

    WHERE (ec_keywords_index.keyword LIKE '%find%' OR ec_keywords_index.keyword LIKE '%my%') 
        AND ec_products.product_id = ec_keyword_relations.related_id 
        AND ec_keyword_relations.application_id = '1' 
        AND ec_products.is_deleted = '0'

    GROUP BY 
        ec_products.product_name

    HAVING 
        (related_keywords LIKE '%find%' AND related_keywords LIKE '%my%')

    LIMIT 
        100

I'm using GROUP_CONCAT to create a column containing all the keywords related to the products, then my WHERE clause is used to ONLY select the keywords that are actually found in my search string... Then the HAVING clause is used to search in column build using GROUP_CONCAT. 我正在使用GROUP_CONCAT创建一个包含与产品相关的所有关键字的列,然后使用我的WHERE子句仅选择在搜索字符串中实际找到的关键字...然后使用HAVING子句在该列中进行搜索使用GROUP_CONCAT构建。

OWN ANSWER WITH EXAMPLE: 拥有示例的答案:

After some fiddling around, I have come up with this solution which works just as expected. 经过一番摆弄之后,我想出了可以按预期工作的解决方案。 I don't know if it is best practice, but it works like a charm. 我不知道这是否是最佳做法,但它就像一个魅力。

    $searchInput = 'find my';
    $keywords = explode(' ', $searchInput);

    $keyword_count = count($keywords);

    $n = 1;
    $where_query = '';
    $having_query = '';

    foreach($keywords as $keyword) { 
        if ($n != $keyword_count) {
            $w_query_ext = ' OR '; 
            $h_query_ext = ' AND ';
        } else { 
            $w_query_ext = ''; 
            $h_query_ext = ''; 
        }

        $where_query .= "ec_keywords_index.keyword LIKE '%" . $keyword . "%'" . $w_query_ext;

        $having_query .= "related_keywords LIKE '%" . $keyword . "%'" . $h_query_ext;

        ++$n;
    }

    $query = "
        SELECT 
            ec_products.product_name, 
            GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'

        FROM 
            ec_products, 
            ec_keywords_index

        LEFT JOIN 
            ec_keyword_relations 
            ON ec_keyword_relations.word_id = ec_keywords_index.word_id

        WHERE ($where_query) 
            AND ec_products.product_id = ec_keyword_relations.related_id 
            AND ec_keyword_relations.application_id = '1' 
            AND ec_products.is_deleted = '0'

        GROUP BY 
            ec_products.product_name

        HAVING 
            ($having_query)

        LIMIT 
            100";

    $stmt = $mysqli->prepare($query);
    $stmt->execute();
    $stmt->store_result();
    $stmt->bind_result($name);
    while ($stmt->fetch()) {
        echo $name;
    }

The above example outputs a query like this: 上面的示例输出如下查询:

    SELECT 
        ec_products.product_name, 
        GROUP_CONCAT(' ', ec_keywords_index.keyword) AS 'related_keywords'

    FROM 
        ec_products, 
        ec_keywords_index

    LEFT JOIN 
        ec_keyword_relations 
        ON ec_keyword_relations.word_id = ec_keywords_index.word_id

    WHERE (ec_keywords_index.keyword LIKE '%find%' OR ec_keywords_index.keyword LIKE '%my%') 
        AND ec_products.product_id = ec_keyword_relations.related_id 
        AND ec_keyword_relations.application_id = '1' 
        AND ec_products.is_deleted = '0'

    GROUP BY 
        ec_products.product_name

    HAVING 
        (related_keywords LIKE '%find%' AND related_keywords LIKE '%my%')

    LIMIT 
        100

I'm using GROUP_CONCAT to create a column containing all the keywords related to the products, then my WHERE clause is used to ONLY select the keywords that are actually found in my search string... Then the HAVING clause is used to search in column build using GROUP_CONCAT. 我正在使用GROUP_CONCAT创建一个包含与产品相关的所有关键字的列,然后使用我的WHERE子句仅选择在搜索字符串中实际找到的关键字...然后使用HAVING子句在该列中进行搜索使用GROUP_CONCAT构建。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM