【问题标题】:Why "t1 inner join t2" and "t1 inner join t3" is fast, but "t1 inner join t2 inner join t3" is 265 times slower?为什么“t1 inner join t2”和“t1 inner join t3”快,但“t1 inner join t2 inner join t3”慢265倍?
【发布时间】:2020-01-09 22:05:57
【问题描述】:

我在我的 PC 上使用 bitnami WAMP 创建了一个 mysql 服务器,并使用流动的 SQL 创建了两个表 table_a 和 table_b:

CREATE TABLE `table_a` (
  `id` int(11) DEFAULT NULL,
  `c_id` varchar(45) DEFAULT NULL,
  `date` date DEFAULT NULL,
  `value` decimal(10,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `table_b` (
  `id` int(11) DEFAULT NULL,
  `c_id` varchar(45) DEFAULT NULL,
  `status` int(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

当我跑步时:

select 
    t1.id, 
    t1.date,
    t1.value
from
    (
    select 
        id,
        c_id,
        date,
        value
    from 
        table_a
    where
        id >= '7000'
    and id <  '10000'
    )
    t1
inner join
    (
    select 
        id, 
        c_id 
    from 
        table_b 
    where 
        status = 1
        id >= '7000'
    and id <  '10000'
    group by 
        id, 
        c_id
    order by null
    ) 
    t2
on 
    t1.id = t2.id
and t1.c_id = t2.c_id

花费 0.031 秒, 这是解释计划:

当我跑步时:

select 
    t1.id, 
    t1.date,
    t1.value
from
    (
    select 
        id,
        c_id,
        date,
        value
    from 
        table_a
    where
        id >= '7000'
    and id <  '10000'
    )
    t1
inner join
(
    select 
        id
    from 
        (

            SELECT 
                id, 
                count(*) as times 
            FROM 
                table_b 
            where 
                status = 1
            and id >= '7000'
            and id <  '10000'   
            group by 
                id 
            order by null

        )
        t 
    where 
        times >= 2
) 
t3
on t1.id = t3.id

仍然需要 0.031 秒, 这是解释计划:

但是当我运行 t1 inner join t2 inner join t3 时,它需要 8.375 秒,这里是 sql:

select 
    t1.id, 
    t1.date,
    t1.value
from
    (
    select 
        id,
        c_id,
        date,
        value
    from 
        table_a
    where
        id >= '7000'
    and id <  '10000'
    )
    t1
inner join
    (
    select 
        id, 
        c_id 
    from 
        table_b 
    where 
        status = 1
        id >= '7000'
    and id <  '10000'
    group by 
        id, 
        c_id
    order by null
    ) 
    t2
on 
    t1.id = t2.id
and t1.c_id = t2.c_id

inner join
(
    select 
        id
    from 
        (

            SELECT 
                id, 
                count(*) as times 
            FROM 
                table_b 
            where 
                status = 1
            and id >= '7000'
            and id <  '10000'   
            group by 
                id 
            order by null

        )
        t 
    where 
        times >= 2
) 
t3
on t1.id = t3.id

这里是解释计划:

问题的原因是什么?

【问题讨论】:

  • 您能否为您的所有查询添加解释计划。
  • 正如@P.Salmon 所说,没有所有这些查询的Explain... 计划,很难进行比较。不过,你可以从这个答案中得到一些关于你的问题的解释:dba.stackexchange.com/a/247171/160363
  • 不是您问题的真正答案,但可能是解决问题的建议 - 尝试创建临时表 (t1, t2) 和 (t1, t3) 并将它们内部连接在一起 - 看看会有多快.它可能会给你一些关于最佳解决方案的答案。
  • 嗨@P.Salmonm,感谢您的评论,我现在添加了解释...计划。
  • 嗨@Madhur Bhaiya,感谢您的评论,我现在添加了解释...计划。

标签: mysql


【解决方案1】:

您的问题是您的表缺少索引。连接键上的索引需要连接才能正常执行。对于第一个查询,MySQL 会自动在存储子查询结果的临时表上创建索引 (auto_key0)。查询 2 的查询计划让我感到惊讶。我本来希望有一个与查询 1 类似的计划,但我不明白显示的计划如何能像报告的那样执行。另外,我不明白为什么您在第三个查询中没有得到 auto_key。

无论如何,对于 InnoDB 表,建议始终定义主键。就您而言,我猜id 您是表中的主键。您的查询也将受益于 c_id 上的二级索引。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-20
    • 2015-03-03
    • 2013-11-11
    • 2018-03-09
    • 2018-07-10
    • 2019-10-09
    • 1970-01-01
    • 2013-02-07
    相关资源
    最近更新 更多