Очень медленный запрос при объединении заказа по id desc и не существует

SELECT
    *
FROM
    `users`
WHERE
    `id` != 1
    AND `users`.`activated` = 1
    AND NOT EXISTS (
        SELECT
            1
        FROM
            `blockings`
        WHERE (blockings.user_id = users.id
            AND blockings.blocked_id = 1)
        OR(blockings.blocked_id = users.id
            AND blockings.user_id = 1))
ORDER BY
    users.id DESC
LIMIT 10 OFFSET 0

Выполнение этого запроса в таблице из 30 тыс. строк занимает 5 секунд.

Это занимает некоторое время, когда я удаляю часть НЕ СУЩЕСТВУЕТ

Существует индекс для пользователей.id и blockings.user_id и blockings.blocked_id

Как я могу ускорить этот запрос?

Вам нужно ВСЕ в ряду? Может быть, вы можете ограничить * выбора ТОЛЬКО нужными столбцами?

easleyfixed 13.04.2023 23:55

@easleyfixed ничего не меняет. по-прежнему занимает 4,5 секунды

Utku Dalmaz 13.04.2023 23:57

Хм, хорошо, так что сам поиск по сравнению с вытягиванием - это задержка ... просто удостоверяюсь. Ну, я собирался предложить индексацию, но она у вас уже есть. Какую версию MySQL вы используете и, возможно, данные на SSD-накопителях?

easleyfixed 13.04.2023 23:59

mysql 8.0.28 aws rds

Utku Dalmaz 14.04.2023 00:01

Задавая вопросы по оптимизации запросов в Stack Overflow, включите в свой запрос выходные данные SHOW CREATE TABLE для каждой таблицы, чтобы нам не приходилось гадать о столбцах, типах данных или индексах. Также включите EXPLAIN для текущего запроса и таблиц.

Bill Karwin 14.04.2023 00:32
Освоение архитектуры микросервисов с 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
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
0
5
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Это что-то вроде «удара в темноте», поскольку вы не включили определения таблиц или вывод EXPLAIN для текущего запроса в свой вопрос.

С индексами одного столбца на blockings.user_id и blockings.blocked_id вы должны увидеть index_merge для blockings в выводе EXPLAIN для вашего текущего запроса.

С PK (user_id, blocked_id) и индексом (blocked_id, user_id) использование UNION в вашем NOT EXISTS, вероятно, будет намного быстрее:

SELECT *
FROM users u
WHERE u.id != 1
  AND u.activated = 1
  AND NOT EXISTS (
        SELECT 1
        FROM blockings b
        WHERE b.user_id = u.id AND b.blocked_id = 1
        UNION ALL
        SELECT 1
        FROM blockings b
        WHERE b.blocked_id = u.id AND b.user_id = 1
    )
ORDER BY u.id DESC
LIMIT 10 OFFSET 0;

Как предложил MatBailie, стоит попробовать разделить UNION ALL на два отдельных NOT EXISTS:

SELECT *
FROM users u
WHERE u.id != 1
AND u.activated = 1
AND NOT EXISTS (
    SELECT 1
    FROM blockings b
    WHERE b.user_id = u.id AND b.blocked_id = 1
)
AND NOT EXISTS (
    SELECT 1
    FROM blockings b
    WHERE b.blocked_id = u.id AND b.user_id = 1
)
ORDER BY u.id DESC
LIMIT 10 OFFSET 0;

Я бы использовал два оператора NOT EXISTS (в сочетании с AND), а не UNION ALL

MatBailie 14.04.2023 11:52

Я попробовал оба и получил немного лучшую производительность (незначительную) от UNION ALL, но я думаю, что это будет зависеть от распределения данных OP. Определенно стоит попробовать оба.

user1191247 14.04.2023 12:05

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

SELECT
    users.id,  /* all the necessary columns goes here */
    users.name,
    users. Email
FROM
    users
    LEFT JOIN blockings ON (
        (blockings.user_id = users.id AND blockings.blocked_id = 1)
        OR (blockings.blocked_id = users.id AND blockings.user_id = 1)
    )
WHERE
    users.id != 1
    AND users.activated = 1
    AND blockings.id IS NULL
ORDER BY
    users.id DESC
LIMIT 10 OFFSET 0;

С операционкой все равно будет проблема.

MatBailie 14.04.2023 11:53

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