【问题标题】:Mysql improve query speed involving multiple tablesMysql提高涉及多表的查询速度
【发布时间】:2023-04-01 09:23:02
【问题描述】:

我有以下问题

SELECT a.id, b.id from table1 AS a, table2 AS b
WHERE a.table2_id IS NULL
AND a.plane = SUBSTRING(b.imb, 1, 20)
AND (a.stat LIKE "f%" OR a.stat LIKE "F%")

这是 EXPLAIN 的输出

+----+-------------+-------+------+------------ -------------------------------------------------- -----------------------------------------+-------- ----------+---------+------+----------+----------- --+
|编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 |
+----+-------------+--------+------+--------------- -------------------------------------------------- --------------------------+------------------------ -------+---------+------+----------+-------------+
| 1 |简单 |乙 |全部 |空 |空 |空 |空 | 28578039 | |
| 1 |简单 |一个 |参考 | index_on_plane,index_on_table2_id_id,mysql_confirmstat_on_stat | index_on_plane | 258 |功能| 2 |使用位置 |
+----+-------------+--------+------+--------------- -------------------------------------------------- --------------------------+------------------------ -------+---------+------+----------+-------------+

执行查询大约需要 80 分钟

table1上的索引如下

+--------------+------------+---------- ------------+-------------+--------------+--------- --+-------------+----------+--------+------+------ ------+---------+----------------+ |表 |非唯一 |键名 | Seq_in_index |列名 |整理 |基数|子部分 |包装 |空 |索引类型 |评论 |索引评论 | +--------------+------------+---------- ------------+-------------+--------------+--------- --+-------------+----------+--------+------+------ ------+---------+----------------+ |表1 | 0 |初级 | 1 |编号 |一个 | 50319117 |空 |空 | | BTREE | | | |表1 | 1 | index_on_post | 1 |发布 |一个 | 7188445 |空 |空 |是 | BTREE | | | |表1 | 1 | index_on_plane | 1 |飞机|一个 | 25159558 |空 |空 |是 | BTREE | | | |表1 | 1 | index_on_table2_id | 1 | table2_id |一个 | 25159558 |空 |空 |是 | BTREE | | | |表1 | 1 | index_on_stat | 1 |统计 |一个 | 187 |空 |空 |是 | BTREE | | | +--------------+------------+---------- ------------+-------------+--------------+--------- --+-------------+----------+--------+------+------ ------+---------+----------------+

和 table2 索引是。

+-------+------------+----------------------------+ --------------+-------------+------------+--------- ----+----------+--------+------+------------+-- ----+---------------+ |表 |非唯一 |键名 | Seq_in_index |列名 |整理 |基数|子部分 |包装 |空 |索引类型 |评论 |索引评论 | +-------+------------+----------------------------+ --------------+-------------+------------+--------- ----+----------+--------+------+------------+-- ----+---------------+ |表2 | 0 |初级 | 1 |编号 |一个 | 28578039 |空 |空 | | BTREE | | | |表2 | 1 | index_on_post | 1 |发布 |一个 | 28578039 |空 |空 |是 | BTREE | | | |表2 | 1 | index_on_ver | 1 |版本 |一个 |第1371章空 |空 |是 | BTREE | | | |表2 | 1 | index_on_imb | 1 | IMB |一个 | 28578039 |空 |空 |是 | BTREE | | | +-------+------------+----------------------------+ --------------+-------------+------------+--------- ----+----------+--------+------+------------+-- ----+---------------+

如何提高这个查询的执行时间?

这里是更新的解释

EXPLAIN SELECT STRAIGHT_JOIN a.id, b.id from table1 AS a JOIN b AS b  
ON a.plane=substring(b.imb,1,20) 
WHERE a.table2_id IS NULL  
AND (a.stat LIKE "f%" OR a.stat LIKE "F%");
+----+-------------+--------+------+--------------- -------------------------------------------------- --------------------------+------------------------ --------+---------+--------+----------+------------ ------------------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+--------+------+--------------- -------------------------------------------------- --------------------------+------------------------ --------+---------+--------+----------+------------ ------------------+ | 1 |简单 |一个 |参考 | index_on_plane,index_on_table2_id,index_on_stat | index_on_table2_id | 5 |常量 | 500543 |使用位置 | | 1 |简单 |乙 |全部 |空 |空 |空 |空 | 28578039 |使用哪里;使用连接缓冲区 | +----+-------------+--------+------+--------------- -------------------------------------------------- --------------------------+------------------------ --------+---------+--------+----------+------------ ------------------+

SQL 小提琴链接http://www.sqlfiddle.com/#!2/362a6/4

【问题讨论】:

  • 尝试加入类似 ` ... from table1 a left join table2 b on a.plane=substring(b.imb,...) 的表 - 你会收到什么?
  • 在解释结果中,S和CS是什么意思。分别是table1和table2吗?
  • 您可以尝试在 table2 上为 table2.imb 的前 20 个字符建立索引。 stackoverflow.com/questions/10595037/…
  • @ursitesion 更新了问题
  • @BrianHoover 在 table2 中的 imb 上已有索引

标签: mysql performance query-optimization


【解决方案1】:

至少在三个方面,您的架构注定了您的查询速度很慢。您将需要修改您的架构以从中获得良好的性能。我可以看到三种方法来修复您的架构。

第一种方式(可能很容易解决):

  a.stat LIKE "f%" OR a.stat LIKE "F%"

这个OR 操作可能会使查询的运行时间加倍。但是,如果您将 stat 列的排序规则设置为不区分大小写的内容,则可以将其更改为

  a.stat LIKE "f%"

您已经在此列上建立了索引。

第二种方式(可能不那么难修复)。该条款明确否定了索引的使用;当涉及 NULL 值时,它们是无用的。

WHERE a.table2_id IS NULL

您能否将table2_id 的定义更改为NOT NULL 并提供一个默认值(可能为零)来指示缺失数据?如果是这样,您将处于良好状态,因为您可以将其更改为使用索引的搜索谓词。

WHERE a.table2_id = 0

第三条路(可能很难)。该子句中函数的存在使在连接中使用索引失败。

WHERE ... a.plane = SUBSTRING(b.imb, 1, 20)

您需要创建一个名为b.plane 的额外列(是的,是的,在Oracle 中它可能是一个函数索引,但谁有这种钱?)或存储在其中的子字符串。

如果你做了所有这些事情并稍微重构你的查询,它会是这样的:

SELECT a.id AS aid, 
       b.id AS bid
  FROM table1 AS a
  JOIN table2 AS b ON a.plane = b.plane /* the new column */
 WHERE a.stat LIKE 'f%'
   AND a.table2_id = 0

最后,您可以通过创建以下复合索引作为查询的覆盖索引来稍微提高性能。如果您不确定这意味着什么,请查看覆盖索引

 table1  (table2_id, stat, plane, id)
 table2  (plane, id)  /* plane is your new column */

覆盖索引需要权衡:它们会减慢插入和更新操作,但会加快查询速度。只有您有足够的信息来明智地做出权衡。

【讨论】:

    【解决方案2】:

    必须为执行连接操作的列建立索引,并且 MySQL 优化器应该使用它以获得更好的性能。它将最小化检查的行数(连接大小)

    试试这个

    SELECT STRAIGHT_JOIN a.id, b.id from table1 AS a JOIN table2 AS b ON a.plane=substring(b.imb,1,20)
    WHERE a.table2__id IS NULL and (a.stat LIKE "f%" OR a.stat LIKE "F%")
    

    首先检查执行计划。如果它甚至没有使用index_on_imb 索引,请创建一个组合table2.imbtable2.id 的复合索引,其中table2.imb 将排在首位。

    【讨论】:

    • 执行计划和之前一样,table2不使用key,能解释一下索引吗?
    • @androidharry:你的意思是定义新的复合索引吗?
    • 修改了答案。请尝试使用它并告诉我结果
    • 这就是你的意思:CREATE INDEX index_on_id_imb ON table2 (id,imb(20));
    • 不,这不是复合索引。在两列上创建索引
    【解决方案3】:

    在这种情况下,派生表可能会提高性能,这取决于索引 index_on_table2_id,index_on_stat..

    SELECT a.id, b.id from table1 AS a, table2 AS b
    WHERE a.table2_id IS NULL
    AND a.plane = SUBSTRING(b.imb, 1, 20)
    AND (a.stat LIKE "f%" OR a.stat LIKE "F%")
    

    可以重写为.. 派生表将强制 MySQL 检查 500543 行,就像最后解释说的那样

    SELECT a.id, b.id
    FROM (SELECT plane FROM table1 WHERE (a.table2_id IS NULL) AND (a.stat LIKE "f%" OR a.stat LIKE "F%")) a
    INNER JOIN table2 b
    ON a.plane = SUBSTRING(b.imb, 1, 20)
    

    【讨论】:

    • 子查询无返回结果时出现“'字段列表'中的未知列'a.id'”错误
    • @androidharry 在此处分享您的表格和一些示例数据sqlfiddle.com
    【解决方案4】:

    除了我对 ID 列的评论之外,您似乎正试图在“平面”而不是 ID 列上回填连接。如果我是正确的,您希望 table2 中的所有记录在 table1 中没有匹配项

    select
          a.id,
          b.id
       from
          table2 b
             left join table1 a
                on b.id = a.table2_id
               AND substr( b.imb, 1, 20 ) = a.plane
               AND (   a.stat LIKE "f%" 
                    OR a.stat LIKE "F%")
       where
          a.table2_id is null
    

    另外,为了帮助索引连接,我会覆盖索引,这样引擎就不必返回原始数据来获取合格记录。

    table1 -- index ( plane, stat, table2_id, id )
    table2 -- index ( imb, id )
    

    但是,请再次说明表连接的基础是否基于键...根据 table1 的示例列具有 table2_id 列,我猜测这与 table2.id 有关。

    进行左连接的目的基本上是说...对于左侧表(在我的示例表 2 中)中的每条记录,以任何标准/条件连接到右侧表(表 1)——现在使用 KEY ID 列作为主要依据,然后是平面和状态设置。

    所以,即使我在 table2_id 上的两个表之间进行连接,如果它确实找到匹配项,它将被排除...只有当它没有找到匹配项时才会包含它。

    最后,由于您隐藏了表格的真实基础,因此您只能猜测帮助者的工作。即使它是“个人”类型的数据,您也没有显示任何数据,只是我如何获取它。对你想要得到的东西有一个更好的心理形象比在有限的上下文中使用虚假的表/列名更好。

    【讨论】:

    • table2_id 是table2中记录的id
    • @androidharry,那么这个查询可能会为您提供显着的性能。
    • 如果我删除 "b.id = a.table2_id" 并使用内部联接,此查询将返回正确的结果,否则我会得到额外的 NULL 行
    猜你喜欢
    • 2012-05-07
    • 2012-03-31
    • 2019-08-13
    • 1970-01-01
    • 2019-10-09
    • 2021-11-17
    • 2021-12-24
    • 1970-01-01
    • 2018-05-17
    相关资源
    最近更新 更多