我对MySQL中的索引很新。我知道,我应该早点把它拿出来,但大多数项目都足够小,我可以逃脱它;)
所以,现在我正在测试它。我通过在查询上运行EXPLAIN
来完成测试:
查询:
EXPLAIN SELECT a . *
FROM `tff__keywords2data` AS a
LEFT JOIN `tff__keywords` AS b ON a.keyword_id = b.id
WHERE (
b.keyword = 'dog' || b.keyword = 'black' || b.keyword = 'and' || b.keyword = 'white'
)
GROUP BY a.data_id
HAVING COUNT( a.data_id ) =4
首先,没有索引我得到了这些结果:
然后,对于data_id和keyword_id的索引,我得到了这个:
据我了解,MySQL必须搜索的行数从61k减少到10k,这一定是好的吗?
所以我的问题是,我在这里是否正确?在尝试优化时还有什么可以考虑的吗?
更新
此外,在AJ和Piskvor的一些帮助指出我的另一个表及其列关键字没有索引后,我得到了这个:
很大改进!正确?
答案 0 :(得分:4)
如您所见,用于表格key
的{{1}}仍为b
。您可能希望在NULL
上添加索引并与
b.keyword
这在功能上与您的WHERE b.keyword IN ('dog','black','and','white')
子句不同,但它返回相同的结果。
看起来,您可能对全文搜索感兴趣。
答案 1 :(得分:3)
根据您要实现的目标,您应该将LEFT JOIN
替换为INNER JOIN
,或将WHERE
条件移至ON
子句:
现在:
SELECT a.*
FROM `tff__keywords2data` AS a
LEFT JOIN
`tff__keywords` AS b
ON b.id = a.keyword_id
WHERE b.keyword = 'dog' || b.keyword = 'black' || b.keyword = 'and' || b.keyword = 'white'
GROUP BY
a.data_id
HAVING COUNT( a.data_id ) = 4
您的查询实际上是INNER
联接(因为WHERE
子句中有非空条件)。
此外,您应该使用原生OR
或IN
构造来代替使用位算术(不可思议):
SELECT a.*
FROM `tff__keywords2data` AS a
JOIN `tff__keywords` AS b
ON b.id = a.keyword_id
WHERE b.keyword IN ('dog', 'black', 'and', 'white')
GROUP BY
a.data_id
HAVING COUNT(*) = 4
您可能还想在ttf__keywords (keyword)
上创建一个索引,该索引可以对您要搜索的keywords
进行过滤,并从前导b
中选择较少的记录。
最后,如果您不需要a.data_id
上的隐式排序,请通过附加ORDER BY NULL
来删除它:
SELECT a.*
FROM `tff__keywords2data` AS a
JOIN `tff__keywords` AS b
ON b.id = a.keyword_id
WHERE b.keyword IN ('dog', 'black', 'and', 'white')
GROUP BY
a.data_id
HAVING COUNT(*) = 4
ORDER BY
NULL
这会从您的计划中移除filesort
。
答案 2 :(得分:1)
是的,这有所改善(但从快速看,我认为可以更好地改进)。你可以看到,查询优化器现在看到AND USING keyword_id索引。它已经将搜索的行从64283减少到10216.但是这仍然使用一个文件库,希望其他人可以澄清类似于SQL Server表扫描?这不好......但我可能错了。
您现在应该可以将表b中的行减少到10216以下
答案 3 :(得分:1)
你正在对b.keyword进行字符串比较....在那里添加一个索引。
答案 4 :(得分:1)
使用INNER JOIN
代替LEFT JOIN
。左连接将返回连接表中不匹配的行,我认为您不需要这些行。
答案 5 :(得分:1)
尝试将索引放在WHERE子句中的所有内容中,以及JOIN中的任何内容,这样就可以了:
a.keyword_id 出价 b.keyword
您可能还想尝试向a.data_id添加索引,因为它位于“GROUP BY”中。索引太多通常不是问题,除非您向大型表添加大量数据 - 这可能导致INSERT非常慢。