【问题标题】:mysql OR will not use indexmysql OR 不会使用索引
【发布时间】:2020-05-21 18:10:55
【问题描述】:

我有一个不使用索引的 mysql 查询

  • 当我使用 system.serial = serials.serial 时,它将使用 system.serial 索引
  • 当我使用 system.serial = LEFT(serials.serial,length(serials.serial)-1) 时,它将使用
    system.serial 索引
  • 当我使用 system.serial = serials.serial 或 system.serial = LEFT(serials.serial,length(serials.serial)-1); 没有使用索引

以下是带有解释结果的查询

谢谢, 瑞恩

示例只有几个联合选择,但实际查询有 ~7k

explain select serials.serial, system.* from 
(SELECT 'S244935X7318894' AS 'serial' 
UNION SELECT 'S244935X6C39166'
UNION SELECT 'S244911X8121539') as serials
left join system on system.serial = serials.serial;

id;select_type;table;type;possible_keys;key;key_len;ref;rows;Extra
1;PRIMARY;<derived2>;ALL;NULL;NULL;NULL;NULL;2;
1;PRIMARY;system;eq_ref;serial_UNIQUE;serial_UNIQUE;182;serials.serial;1;"Using where"
2;DERIVED;NULL;NULL;NULL;NULL;NULL;NULL;NULL;"No tables used"
3;UNION;NULL;NULL;NULL;NULL;NULL;NULL;NULL;"No tables used"
4;UNION;NULL;NULL;NULL;NULL;NULL;NULL;NULL;"No tables used"
NULL;"UNION RESULT";<union2,3,4>;ALL;NULL;NULL;NULL;NULL;NULL;



explain select serials.serial, system.* from 
(SELECT 'S244935X7318894' AS 'serial'
UNION SELECT 'S244935X6C39166'
UNION SELECT 'S315185X9703017'
UNION SELECT 'S384860X9A15080A'
UNION SELECT 'S290000X0307210 A2 BLADE') as serials
left join system on  system.serial = LEFT(serials.serial,length(serials.serial)-1) ;

id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,<derived2>,ALL,NULL,NULL,NULL,NULL,2,
1,PRIMARY,system,eq_ref,serial_UNIQUE,serial_UNIQUE,182,func,1,"Using where"
2,DERIVED,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
3,UNION,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
4,UNION,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
5,UNION,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
6,UNION,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
NULL,"UNION RESULT","<union2,3,4,5,6>",ALL,NULL,NULL,NULL,NULL,NULL,


explain select serials.serial, system.* from 
(SELECT 'S244935X7318894' AS 'serial' 
UNION SELECT 'S244935X6C39166'
UNION SELECT 'S244911X8121539') as serials
left join system on system.serial = serials.serial or system.serial = LEFT(serials.serial,length(serials.serial)-1);


id,select_type,table,type,possible_keys,key,key_len,ref,rows,Extra
1,PRIMARY,<derived2>,ALL,NULL,NULL,NULL,NULL,2,
1,PRIMARY,system,ALL,serial_UNIQUE,NULL,NULL,NULL,190694,"Range checked for each record (index map: 0x4)"
2,DERIVED,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
3,UNION,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
4,UNION,NULL,NULL,NULL,NULL,NULL,NULL,NULL,"No tables used"
NULL,"UNION RESULT","<union2,3,4>",ALL,NULL,NULL,NULL,NULL,NULL,

【问题讨论】:

  • 是的,你不应该期望 MySQL 引擎支持 IXOR 操作。它仍然是一个简单的引擎。
  • 我总是参考mysql.rjweb.org/doc.php/index_cookbook_mysql#or 来解决索引问题。我已经有一段时间没有看它了,但是通读它应该可以让您深入了解如何利用索引进行此查询。

标签: mysql select indexing


【解决方案1】:
select serials.serial, system.* from 
(SELECT 'S244935X7318894' AS 'serial' 
UNION SELECT 'S244935X6C39166'
UNION SELECT 'S244911X8121539') as serials
left join system on system.serial = serials.serial;

最好这样做:

SELECT *
    FROM system
    WHERE serial IN ('S244935X7318894', ...);

还有INDEX(serial)

在其他情况下,UNION ALLUNION DISTINCT 更快,因为它避免了重复数据删除过程。

(在最后一种情况下,我同意OR 上的其他人。)

对于第三种情况(使用OR):构建一个临时表,执行LEFT然后JOIN system。对于 MySQL 8.0 或 MariaDB 10.2,最好使用非递归 CTE

【讨论】:

    【解决方案2】:

    MySQL 是一个很棒的免费引擎。它已经存在很久了,现在它支持很多 SQL 功能。

    您正在寻找的是Index Merge Union 操作。尽管已实现,但优化器似乎并不经常使用它(如@RickJames 所述进行验证)。

    在任何情况下,提升(从不确保)索引使用的一个选项是使用UNION ALL 重写您的查询。例如,不要写:

    SELECT * FROM my_table WHERE col1 = 123 OR col1 = 456
    

    可能不会使用索引,您可以将其改写为:

    SELECT * FROM my_table WHERE col1 = 123
    UNION ALL
    SELECT * FROM my_table WHERE col1 = 456
    

    此查询的两个部分都可以在 MySQL 中使用简单的“索引查找”(或其他情况下的“索引范围扫描”)。

    【讨论】:

    • “索引合并联合”已实现,但几乎从未调用过。类似的“索引合并相交”也很少使用,而且几乎总是不如复合索引。同时,UNION ALL(如上所述)或UNION DISTINCT(如果可能发生重复)是一个很好的解决方法。
    • @RickJames 我是正确的。从未偶然发现“索引边缘优化”(dev.mysql.com/doc/refman/8.0/en/index-merge-optimization.html)。现在,就为什么通常不调用而言,也许成本优化器会提前将其排除在外。很有趣。
    • 这是我的想法。要做Index Merge Union,需要从一个索引的搜索中得到一堆主键,然后从另一个索引中得到另一串。组合这两个列表,对组合列表进行去重,然后最终获取所需的行。这可能涉及一些临时表(或至少在 RAM 中 malloc 的结构)。大量工作只为最低限度的利益。
    • 认为其他引擎(Oracle、SQL Server 等)可能从索引合并联合/相交中受益更多:它们不会通过PRIMARY KEY,而是一个“rownum”。也就是说,他们的优化器已经在考虑 rownums 了。索引合并是在 MySQL 至少 10 年之后添加到 MySQL 中的。从那时起,我查看了成千上万的解释,几乎从未见过这样的。
    猜你喜欢
    • 2012-12-03
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    • 2020-02-17
    • 1970-01-01
    • 2012-03-06
    • 2011-02-19
    • 1970-01-01
    相关资源
    最近更新 更多