我想知道我的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)
我应该改变什么吗?
答案 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