Почему мой индекс игнорируется при определенных обстоятельствах, что приводит к замедлению выполнения. Я нашел причину в сочетании использования UNION ALL + выбранного API базы данных PHP + выбранной кодировки + использованной реализации привязки параметров.
Настройка
Я создал представление my_view, которое выглядит следующим образом:
Код: Выделить всё
SELECT type FROM table1
UNION ALL
SELECT type FROM table2
Код: Выделить всё
CREATE TABLE table1
(
id INT UNSIGNED AUTO_INCREMENT,
type INT NOT NULL,
... -- some more data columns
PRIMARY KEY (id)
);
CREATE INDEX my_idx ON table1 (type);
Код: Выделить всё
SELECT * FROM my_view WHERE type = 1;
Код: Выделить всё
┌────┬─────────────┬────────────┬───────┬───────────────┬────────┬─────────┬────────┬──────┬─────────────┐
│ id │ select_type │ table │ type │ possible_keys │ key │ key_len │ ref │ rows │ Extra │
╞════╪═════════════╪════════════╪═══════╪═══════════════╪════════╪═════════╪════════╪══════╪═════════════╡
│ 1 │ PRIMARY │ │ ALL │ │ │ │ │ 110 │ Using where │
│ 2 │ DERIVED │ table1 │ const │ my_idx │ my_idx │ 5 │ const │ 72 │ Using index │
│ 3 │ UNION │ table2 │ const │ my_idx │ my_idx │ 5 │ const │ 38 │ Using index │
└────┴─────────────┴────────────┴───────┴───────────────┴────────┴─────────┴────────┴──────┴─────────────┘
Проблема
Это меняется, когда я использую PHP для выполнения запроса. В зависимости от API подключения к базе данных (например, mysqli, PDO) моя база данных решает не использовать индекс для моего представления, используя тот же запрос, что и раньше:
Код: Выделить всё
┌────┬─────────────┬────────────┬───────┬───────────────┬────────┬─────────┬─────┬──────────┬─────────────┐
│ id │ select_type │ table │ type │ possible_keys │ key │ key_len │ ref │ rows │ Extra │
╞════╪═════════════╪════════════╪═══════╪═══════════════╪════════╪═════════╪═════╪══════════╪═════════════╡
│ 1 │ PRIMARY │ │ ALL │ │ │ │ │ 56247595 │ Using where │
│ 2 │ DERIVED │ table1 │ index │ │ my_idx │ 5 │ │ 34706361 │ Using index │
│ 3 │ UNION │ table2 │ index │ │ my_idx │ 5 │ │ 21541234 │ Using index │
└────┴─────────────┴────────────┴───────┴───────────────┴────────┴─────────┴─────┴──────────┴─────────────┘
Моя отладка показала, что следующие комбинации используют индекс (
API
кодировка
дополнительно (методы/опции)
mysqli
Код: Выделить всё
latin1Код: Выделить всё
->execute_query($sql, [$type])mysqli
Код: Выделить всё
latin1Код: Выделить всё
->prepare(); ->bind_param()mysqli
Код: Выделить всё
->set_charset('utf8mb4')Код: Выделить всё
->execute_query($sql, [$type])mysqli
Код: Выделить всё
->set_charset('utf8mb4')Код: Выделить всё
->prepare(); ->bind_param()PDO
Код: Выделить всё
latin1Код: Выделить всё
ATTR_EMULATE_PREPARES = truePDO
Код: Выделить всё
latin1Код: Выделить всё
->setAttribute(PDO::ATTR_EMULATE_PREPARES, false)PDO
Код: Выделить всё
new PDO("...charset=utf8mb4", ...)Код: Выделить всё
ATTR_EMULATE_PREPARES = truePDO
Код: Выделить всё
new PDO("...charset=utf8mb4", ...)Код: Выделить всё
->setAttribute(PDO::ATTR_EMULATE_PREPARES, false)Я не могу понять смысла этого. Что здесь происходит?
Информация о версии
- MariaDB 10.6.18
< li>PHP 8.2.26
Код: Выделить всё
┌───────┬──────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Level │ Code │ Message │
├───────┼──────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Note │ 1003 │ /* select#1 */ select `my_view`.`type` AS `type` from `my_database`.`my_view` where `my_view`.`type` = 1 │
└───────┴──────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Запрос:
Код: Выделить всё
EXPLAIN EXTENDED SELECT * FROM (
SELECT type from table1 UNION ALL SELECT type from table2
) AS q WHERE type = ?;
SHOW WARNINGS;
Результаты:
Вывод всех тестов, отмеченных значкомКод: Выделить всё
WHEREусловие Код: Выделить всё
/* select#1 */
SELECT q.type AS type
FROM (
/* select#2 */
SELECT table1.type AS type
FROM my_database.table1
WHERE table1.type = 1
UNION ALL
/* select#3 */
SELECT table2.type AS type
FROM my_database.table2
WHERE table2.type = 1
) q
WHERE q.type = 1;
Код: Выделить всё
WHEREКод: Выделить всё
/* select#1 */
SELECT q.type AS type
FROM (
/* select#2 */
SELECT table1.type AS type
FROM my_database.table1
UNION ALL
/* select#3 */
SELECT table2.type AS type
FROM my_database.table2
) q
WHERE q.type = 1;
https://phpize.online/sql/mariadb115/a1 ... 56833df9b/
Я отправил отчет об ошибке, который можно найти здесь:
https://jira.mariadb.org/browse/MDEV-35561
Трассы оптимизатора
Я включил трассировку оптимизатора MariaDB, установив для оптимизированного_trace значение Enabled=on. После выполнения запросов я индивидуально проверил трассировку с помощью
Код: Выделить всё
SELECT * FROM information_schema.optimizer_trace
https://www.diffchecker.com/DX1zel8b/
Подробнее здесь: https://stackoverflow.com/questions/792 ... ng-charset
Мобильная версия