我想知道下面的MySql SELECT查询是否需要O(N)或O(logN)。
假设我们有一个表表示4个整数区间[startNum,endNum],并且该表由startNum和endNum列索引。
startNum, endNum
3, 8
10, 15
16, 21
28, 42
质询:
SELECT * from table
where startNum <= 19 AND endNum >= 19
我认为MySql需要O(N),因为它
1. find the first 3 rows using the "startNum"; then
2. go through each of them and use the "endNum" to identify the 3rd row; then
3. return the 3rd row [16, 21] as the result.
MySql是否足够"聪明",能够做到以下几点?
1. binary search on the startNum to find the position of the 3rd row, since "startNum" is sorted; then
2. binary search on the endNum to find the 3rd row again, since "endNum" is also sorted; then
3. return the 3rd row [16, 21] as the result.
根据本文档:https://dev.mysql.com/doc/refman/5.7/en/range-optimization.html
如果运算符是〉、〈、〉=、〈=、!=、〈〉、BETWEEN或LIKE,则优化器使用它,但不再考虑关键部分。
我不认为MySql是做"智能"二进制搜索。
我说的对吗?有没有什么配置可以让MySql做二进制搜索?
2条答案
按热度按时间jhdbpxl91#
这是O(n),如果你在startNum和endNum上都有索引,那么查询计划器会选择一个索引,然后根据表的统计信息,选择一个更具选择性的索引。
然后它将随机访问该索引到第一个符合条件的行,并继续扫描表的其余部分以满足另一个不等式 predicate 。这是BTREE索引的本质。这种情况在使用BTREE索引的每个表服务器中都是相同的,而不仅仅是MySql / Mariadb。
如果索引是复合索引,如下所示
查询计划器可能会选择扫描索引而不是整个表,这通常更快,但仍然是O(n)。
请记住,在性能关键型查询中使用
SELECT *
是一种反模式,除非您确定需要表中的每一列。swvgeqrz2#
将查询转换为O(1)是可能的,但是它要求开始和结束的范围不重叠,并且,如果您愿意为间隙增加额外的行,您可以(并且应该)删除其中的一列。
查询将变为
如果你可能什么也得不到,那么这是解决这些方式之一:
endnum
,则验证返回的endnum
是否为< 19
。LIMIT
的 after(如果没有像这样混乱的语法,就无法在之前完成):endnum
。在这种情况下,您得到的是有效行还是“间隙”行应该是显而易见的。(这里假设您保留startnum
;类似的查询可以写成:you prefer to keep onlyendnum
。)注意,所有可能的startnum值都在 some 行中表示为〉=当前startnum和我在我的博客中讨论了如何处理IP地址范围:http://mysql.rjweb.org/doc.php/ipranges