Запрос с использованием подзапроса требует больше времени, чем тот же запрос с фиксированными данными вместо подзапроса - PullRequest
1 голос
/ 16 июня 2019

комбинированный запрос

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,3 с.

Есть индекс над (a, b) на A.

Анализ комбинированного запроса:

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 использует только часть индекса и игнорирует 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 соответствует размеру результирующего набора для обоих запросов (комбинированный и фиксированный).

Как я могу заставить MariaDB использовать тот же план запроса, что и в фиксированном запросе, используя как a, так и подзапрос b из индекса A?

Ответы [ 2 ]

1 голос
/ 16 июня 2019

Иногда запросы оптимизируются по-разному.Планировщик запросов знает намного больше о том, что вы делаете, когда у вас есть фиксированный список значений.

Если у вас нет дубликатов в 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);
0 голосов
/ 22 июня 2019

Мой мозг превратился в кашу, пытаясь обработать B против b и A против 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)

Таким образом,Оптимизатор может работать независимо от того, с какой таблицы он начинается.Оптимизатор выберет лучшую таблицу для начала, основываясь на статистике для a и c.Однако, если вы не включили «гистограммы» (MariaDB 10.0 - 10.3; в 10.4 они включены по умолчанию), оптимизатор может не иметь полной истории.

...