使用子查询的查询需要比使用固定数据而不是子查询的相同查询更长的时间

时间:2019-06-16 18:10:02

标签: mysql sql indexing subquery mariadb

组合查询

select a, b from A where a > 5 and b in (select b from B where c = "some")

花费比固定查询长30倍的时间

select a, b from A where a > 5 and b in (1, 2, 3)

尽管

  • select b from B where c = "some"产生与固定查询(1, 2, 3)
  • 中使用的完全相同的行集
  • select b from B where c = "some"仅需0.01秒即可执行
  • select a, b from A where a > 5的执行时间为0.3s。

在A的(a,b)上有一个索引。

分析组合查询:

analyze select a, b from A where a > 5 and b in (select b from B)
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: A
         type: range
possible_keys: idx_a_b
          key: idx_a_b
      key_len: 8
          ref: NULL
         rows: 126459
       r_rows: 66181.00
     filtered: 100.00
   r_filtered: 100.00
        Extra: Using index condition; Using temporary; Using filesort
*************************** 2. row ***************************
           id: 1
  select_type: PRIMARY
        table: B
         type: eq_ref
possible_keys: PRIMARY
          key: PRIMARY
      key_len: 2
          ref: A.b
         rows: 1
       r_rows: 1.00
     filtered: 100.00
   r_filtered: 0.09
        Extra: Using where

请注意,r_rows = 66181与select a, b from A where a > 5相匹配。

好像MariaDB仅使用索引的a部分,而忽略了b应该在第一步中从子查询中获取的b。说明扩展显示MariaDB用

替换了我的查询
select b, a from B join A where ((B.b = A.b) and (A.a > 5) and (B.c = "some"))

如果给定的是固定值(1、2、3)(由子查询返回),而不是子查询本身,那么奇怪的是,MariaDB确实确实同时使用了索引的a和b通过分析固定查询来观察:

analyze select a, b from A where a > 5 and y in (1, 2, 3)
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: A
         type: range
possible_keys: idx_a_b
          key: idx_a_b
      key_len: 10
          ref: NULL
         rows: 126459
       r_rows: 59.00
     filtered: 100.00
   r_filtered: 100.00
        Extra: Using index condition; Using temporary; Using filesort

r_rows = 59匹配两个查询(组合查询和固定查询)的结果集大小。

如何通过使用A的索引中的a和子查询b来使MariaDB使用与固定查询中相同的查询计划?

2 个答案:

答案 0 :(得分:1)

有时,查询的优化方式有所不同。当您拥有固定的值列表时,查询计划者会更多地了解您在做什么。

如果b中没有重复项,那么join通常会制定出良好的执行计划:

select a.a, a.b
from a join
     b
     on a.b = b.b
where a.a > 5;

我还建议改为尝试exists

select a, b
from A
where a > 5 and
      exists (select 1 from B where b.b = a.b) ;

并确保您在b(b)上有一个索引:

create index idx_b_b on b(b);

答案 1 :(得分:0)

我的大脑变得笨拙,试图处理B vs b和A vs a,所以我更改了表名。

select  Y.b, Y.a
    from  X
    join  Y
    where  ((X.b = Y.b)
       and  (Y.a > 5)
       and  (X.c = "some")
           )

该查询需要

X:  INDEX(c, b)
Y:  INDEX(a, b), INDEX(b, a)

这样,无论优化器以哪个表开头,它都可以高效运行。优化器将根据ac的统计信息选择更好的表作为开始。但是,除非您打开“直方图”(MariaDB 10.0-10.3;默认为10.4启用),否则优化器可能没有完整的故事。