Функциональный индекс MySQL с IS NULL

У меня есть большая таблица в MySQL с медленным запросом, отчасти из-за такого условия, как WHERE sent_at IS NULL. sent_at — это столбец даты и времени, допускающий значение NULL, но здесь мы хотим только проверить, имеет ли он значение NULL, поэтому стандартный индекс для всех значений был бы расточительным.

Я обнаружил, что создание функционального индекса с помощью ISNULL() или IS NULL работает, но никогда не используется для запросов. Например.

ALTER TABLE users ADD INDEX index_users_sent_at_is_null ((ISNULL(sent_at)));

Далее следует запрос:

SELECT COUNT(*) FROM users WHERE (sent_at IS NULL);

Будет иметь вывод (JSON) EXPLAIN с access_type: "ALL" и отсутствием возможных или выбранных ключей.

Выполнение эквивалента для YEAR(sent_at) работает, как и ожидалось, с использованием соответствующего функционального индекса, поэтому в IS NULL есть что-то особенное, даже несмотря на то, что виртуальный скрытый столбец, который правильно создает функциональный индекс, содержит 0 или 1.

Есть ли элегантный способ заставить это работать?

Вы проверили мощность индекса? Поможет ли вам изменить индексное выражение на sent_at IS NULL (в идеале обе формы должны быть эквивалентны, но мы говорим о MySQL).

Barmar 17.06.2024 22:10

Этот индекс может иметь два возможных значения: true или false. Избирательность такого индекса, вероятно, слишком низка, чтобы MySQL мог его учесть.

Shadow 17.06.2024 22:29

@Shadow Я ожидаю, что объяснение по-прежнему покажет это как возможный ключ.

ysth 17.06.2024 23:48

кажется невозможным индексировать значение null без сгенерированного столбца (или в законопроекте об обходном решении предлагается): dbfiddle.uk/060bclIz

ysth 17.06.2024 23:56
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
2
4
78
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я протестировал ваш пример индекса с MySQL 8.0.37 на своем локальном Macbook. Могу воспроизвести описанную вами проблему:

mysql> explain SELECT COUNT(*) FROM users WHERE (sent_at IS NULL)\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: Using where

Это связано с https://bugs.mysql.com/bug.php?id=99775, где функциональный индекс не используется, когда вы используете выражение в предложении WHERE без сравнения результата с конкретным значением. Эта ошибка была проверена в 2020 году, но еще не исправлена ​​(по состоянию на июнь 2024 года).

В качестве обходного пути вы можете сравнить выражение с определенным постоянным значением, а затем запрос сможет использовать индекс:

mysql> explain SELECT COUNT(*) FROM users WHERE (sent_at IS NULL) = 1\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ref
possible_keys: index_users_sent_at_is_null
          key: index_users_sent_at_is_null
      key_len: 4
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL

Вы можете подумать, что логически эти запросы одинаковы, и вам не нужно использовать обходной путь для оптимизации. Да, я согласен. Вот почему это ошибка.

Источником этой ошибки является отсутствие типа данных BOOLEAN в MySQL. Тип данных выражения (sent_at IS NULL) не несуществующий BOOLEAN, а INTEGER (т. е. 4-байтовое число). Мы можем убедиться в этом, исследуя индексную часть файла тела таблицы: в ней хранится 80 00 00 00 для TRUE и 00 00 00 00 для FALSE. Таким образом, формально серверу необходимо применить функцию к значению выражения, которое хранится в индексе, и определить, не является ли значение FALSE/NULL. Вместо немедленного сравнения с каким-то определенным значением.

Akina 18.06.2024 07:32

Другие вопросы по теме

Медленный запрос MySQL с несколькими подзапросами в предложенииwhere
Оптимизация запросов MySQL (5.7.26), почему select * выполняется намного быстрее, чем select id в моем ведущем запросе с подстановочными знаками?
Проблемы с производительностью рекурсивной хранимой процедуры SQL Server для иерархических данных
Медленная производительность запросов из-за разделов
Анализ данных о продажах: рассчитайте процент каждого продукта от общего объема продаж
Почему удаление столбца из индекса улучшило производительность?
Повышает ли «ограничение 1» производительность запросов столбцов с уникальными значениями?
Как я могу объединить две таблицы, если столбцы, к которым я присоединяюсь, имеют разные типы данных?
Является ли в MySQL крайний левый префикс составного индекса столь же производительным, как и несоставной индекс?
Оптимизировать поток SQL-запросов