Как заставить mariaDB использовать индекс? - PullRequest
0 голосов
/ 21 мая 2019

Я пытаюсь выполнить SQL-запрос.К сожалению, он не использует индекс и вместо этого выполняет сканирование таблицы.

Я уже создал следующие индексы:

  • PRIMARY ($ phone, $$ fc_date)
  • idx $$ fc_status_detail
  • idx $$ fc_date
  • idx $$ fc_status
  • idx $$ телефон

Дополнительно ядублировал таблицу, но это также не дало никаких полезных результатов.

Это структура таблицы:

+--------------------+--------------+------+-----+---------+-------+
| Field              | Type         | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| $id                | varchar(100) | NO   |     | NULL    |       |
| $created_date      | varchar(100) | YES  |     | NULL    |       |
| $phone             | varchar(100) | NO   | PRI | NULL    |       |
| $source            | varchar(100) | YES  |     | NULL    |       |
| Orga               | varchar(100) | YES  |     | NULL    |       |
| Anrede             | varchar(100) | YES  |     | NULL    |       |
| Vorname            | varchar(100) | YES  |     | NULL    |       |
| Zuname             | varchar(100) | YES  |     | NULL    |       |
| Strasse            | varchar(100) | YES  |     | NULL    |       |
| PLZ                | varchar(100) | YES  |     | NULL    |       |
| Ort                | varchar(100) | YES  |     | NULL    |       |
| Geburtsdatum       | varchar(100) | YES  |     | NULL    |       |
| Email              | varchar(100) | YES  |     | NULL    |       |
| Zeitschrift        | varchar(100) | YES  |     | NULL    |       |
| Herkunft           | varchar(100) | YES  |     | NULL    |       |
| Zeitschrift_Titel  | varchar(100) | YES  |     | NULL    |       |
| telefon            | varchar(100) | YES  |     | NULL    |       |
| Stornogrund        | varchar(100) | YES  |     | NULL    |       |
| Storno             | varchar(100) | YES  |     | NULL    |       |
| Telefonnummer      | varchar(100) | YES  |     | NULL    |       |
| Postleitzahl       | varchar(100) | YES  |     | NULL    |       |
| Geburtsjahr        | varchar(100) | YES  |     | NULL    |       |
| $$fc_task          | varchar(100) | YES  |     | NULL    |       |
| $$fc_user          | varchar(100) | YES  |     | NULL    |       |
| $$fc_date          | varchar(100) | NO   | PRI | NULL    |       |
| $$fc_status        | varchar(100) | YES  | MUL | NULL    |       |
| $$fc_status_detail | varchar(100) | YES  | MUL | NULL    |       |
| $$qc_task          | varchar(100) | YES  |     | NULL    |       |
| $$qc_user          | varchar(100) | YES  |     | NULL    |       |
| $$qc_date          | varchar(100) | YES  |     | NULL    |       |
| $$qc_status        | varchar(100) | YES  |     | NULL    |       |
| $$qc_status_detail | varchar(100) | YES  |     | NULL    |       |
| $call_duration     | smallint(6)  | YES  |     | NULL    |       |
| $call_attempts     | smallint(6)  | YES  |     | NULL    |       |
+--------------------+--------------+------+-----+---------+-------+

Это запрос:

EXPLAIN SELECT
    count(*) as total,
    CONCAT(case when c1.$$fc_date < 240 then "short" else "long" end, "/", c1.$$fc_status, "/", c1.$$fc_status_detail) as ergebnis,
    sum(case when c2.$$fc_status = 'success' then 1 else 0 end)/ count(*) as c2_succes_rate
FROM
    contacts c1 FORCE INDEX (PRIMARY),
    contacts_copy c2
WHERE
    c1.$phone = c2.$phone
    and c1.$$fc_date < c2.$$fc_date
group by
    ergebnis

Этоэто результат:

+------+-------------+-------+------+--------------------------------------------------------------+---------+---------+---------------+---------+---------------------------------+
| id   | select_type | table | type | possible_keys                                                | key     | key_len | ref           | rows    | Extra                           |
+------+-------------+-------+------+--------------------------------------------------------------+---------+---------+---------------+---------+---------------------------------+
|    1 | SIMPLE      | c1    | ALL  | PRIMARY                                                      | NULL    | NULL    | NULL          | 2017450 | Using temporary; Using filesort |
|    1 | SIMPLE      | c2    | ref  | PRIMARY,contacts_copy_$phone_IDX,contacts_copy_$$fc_date_IDX | PRIMARY | 402     | nmv.c1.$phone |       1 | Using where                     |
+------+-------------+-------+------+--------------------------------------------------------------+---------+---------+---------------+---------+---------------------------------+

Как вы можете видеть в строке 1, хотя он распознает первичный ключ, он не использует его.Проблема в том, что он сканирует 2 млн.строк и запрос длится не менее 5 минут.

Кто-нибудь может объяснить, в чем может быть проблема?

1 Ответ

1 голос
/ 22 мая 2019

Вы используете InnoDB, правильно? Вторичные ключи InnoDB (такие как INDEX(phone)) неявно включают столбцы PRIMARY KEY. Таким образом, индекс фактически равен BTree (phone, fc_date).

.

Далее, отметим, что необходим еще один столбец из c2: fc_status. Итак, Оптимизатор посмотрел на два способа выполнения запроса. Во-первых, обратите внимание, что любой подход имеет оптимальные столбцы в индексе, и они находятся в оптимальном порядке.

План A: используйте индекс, затем отскок назад и вперед между индексом и данными.

План Б: Сканирование таблицы, для которого не потребуется перемотка вперед и назад.

Оптимизатор правильно выбрал B.

У вас может быть лучший индекс, и оптимизатор, скорее всего, его выберет. И это будет быстрее:

INDEX(phone, fc_date, fc_status)  -- in this order

Это «покрытие», в котором присутствуют все необходимые столбцы. Следовательно, туда и обратно не будет.

Мне нужно критиковать VARCHAR(100). Это потенциально очень плохо для дат, поскольку в зависимости от формата они могут сортироваться неправильно.

Имена, вероятно, никогда не бывают 100 символов; письма могут быть длиннее. И т.д. 4 телефонных номера? Что случилось?

...