MySQL:标签和搜索

时间:2014-08-20 08:54:19

标签: mysql sql

我想知道我的SQL查询是否正确,并且对于大量数据会很快。

SQL结构:

TABLE: products
id            integer auto_increment
name          text
description   text

TABLE: tags
id      integer auto_increment
title   text

TABLE: products_tags
product_id   integer
tag_id       integer

例如,我想找到所有标签ID为1和2或3的产品。

SQL查询:

SELECT DISTINCT products.*
FROM products
LEFT JOIN products_tags ON products_tags.product_id = products.id
WHERE (products_tags.tag_id = 1 AND products_tags.tag_id = 2) OR products_tags.tag_id = 3

我知道我可以使用此查询,但我认为对于大数据来说这将非常慢:

SELECT products.*
FROM products
WHERE products.id IN (SELECT products_tags.product_id FROM products_tags WHERE (products_tags.tag_id = 1 AND products_tags.tag_id = 2) OR products_tags.tag_id = 3)

我应该改变什么吗?

1 个答案:

答案 0 :(得分:1)

您当前的And条款不起作用:

products_tags.tag_id = 1 AND products_tags.tag_id = 2

标签ID不能同时为1和2,这是不可能的。相反,你希望它在可能的值集合中。

如果您要对products_tags表进行过滤而不允许空值,则应将其设为INNER JOIN,而不是LEFT JOIN

添加GROUP BY子句通常比尝试使用WHERE EXISTS或使事情复杂化要快得多。

SELECT P.*
FROM products AS P
INNER JOIN products_tags AS PT
    ON PT.product_id = P.id
WHERE PT.tag_id IN (1,2,3)
GROUP BY P.id

如果您的速度仍然很慢,请在前面按EXPLAIN运行,如下所示:

EXPLAIN
SELECT P.*
FROM products AS P
INNER JOIN products_tags AS PT
    ON PT.product_id = P.id
WHERE PT.tag_id IN (1,2,3)
GROUP BY P.id

这应该说它正在使用索引:

+----+-------------+-------+--------+---------------+---------------+---------+--------------------+------+---------------------------+
| id | select_type | table | type   | possible_keys | key           | key_len | ref                | rows | Extra                     |
+----+-------------+-------+--------+---------------+---------------+---------+--------------------+------+---------------------------+
|  1 | SIMPLE      | PT    | index  | idxProductTag | idxProductTag | 8       | NULL               |    7 | Using where; Using index; |
|  1 | SIMPLE      | P     | eq_ref | PRIMARY       | PRIMARY       | 4       | test.PT.product_id |    1 |                           |
+----+-------------+-------+--------+---------------+---------------+---------+--------------------+------+---------------------------+

如果没有,你可以为此目的创建一个:

 CREATE INDEX idxProductTag ON product_tags (product_id, tag_id);

希望有所帮助。

编辑:看来我对查询的目标有误,这应该更准确:

SELECT P.*
FROM products AS P
INNER JOIN ( -- Sub Query
    SELECT
        product_id
    FROM
        products_tags
    WHERE tag_id = 3 -- Any where the tag_id is 3 should be returned
    UNION
    SELECT
        product_id
    FROM
        products_tags
    WHERE tag_id IN (1,2) -- And any where the tag ID is 1 or 2
    GROUP BY product_id
    HAVING COUNT(1) = 2 -- With exactly 2 rows :D
) AS PT
    ON PT.product_id = P.id
GROUP BY P.id