Пагинация запроса mysql + php занимает 20-40 секунд

0

Привет, у меня есть разбиение на страницы в угловых js в моем приложении, я отправляю данные в свой большой запрос, который включает фильтры, которые пользователь установил

UPDATE SQL_CALC_FOUND_ROWS это моя проблема. Как подсчитать строки определенных фильтров. Мне понадобилось 2 секунды для 100 000 строк Мне нужен номер для разбивки на страницы в виде общего числа

UPDATE: У меня есть следующий внутренний запрос, который я пропустил здесь:

(выберите count (*) от студентов как inner_st, где st.name = inner_st.name) в качестве имен,

когда я удаляю внутренний запрос намного быстрее

строки: 50 000 Таблица пользователей: 4 строки Таблица классов: 4 строки индексов: только id как первичный ключ

время запроса 20-40 секунд

tables: students.

columns : id, date ,class, name,image,status,user_id,active 

table user
coloumn: id,full_name,is_admin

запрос

SELECT  SQL_CALC_FOUND_ROWS st.id, 
        st.date,
        st.image,
        st.user_id,
        st.status,
        st, 
        ck.name AS class_name,
        users.full_name,
        (select count(*) from students AS inner_st where st.name = inner_st.name) AS names,
FROM students AS st
    LEFT JOIN  users ON st.user_id = users.user_id
    LEFT JOIN  classes AS ck  ON st.class = ck.id
WHERE date BETWEEN '2018-01-17' AND DATE_ADD('2018-01-17', INTERVAL 1 DAY)
    AND DATE_FORMAT(date,'%H:%i') >= '00:00'
    AND DATE_FORMAT(date,'%H:%i') <= '23:59'
    AND st.active=1 
    -- here I can concat filters from  web like "and class= 1"
ORDER BY st.date DESC
LIMIT  0, 10

Как я могу сделать это быстрее? когда я удаляю порядок и SQL_CALC_FOUND_ROWS быстрее, но я нуждаюсь в них, я слышал об индексах, но только первичный ключ - индекс

  • 0
    Трудно сказать, не зная, сколько строк в ваших таблицах, какие столбцы индексируются.
  • 0
    @JoeP. привет - идентификатор в качестве первичного ключа и 50000 строк
Показать ещё 3 комментария
Теги:
indexing

2 ответа

1

Несколько комментариев, прежде чем рекомендовать другой подход к этому запросу:

  • Вы SQL_CALC_FOUND_ROWS удалить SQL_CALC_FOUND_ROWS и вместо этого запустить два запроса (один, который подсчитывает, и тот, который выбирает данные)? В некоторых случаях это может быть быстрее, чем объединение их обоих в один запрос.
  • Какова цель этих условий? Чего вы пытаетесь достичь? Можем ли мы удалить их (как кажется, они всегда могут возвращать true?) - AND DATE_FORMAT(st.date, '%H:%i') >= '00:00' AND DATE_FORMAT(st.date, '%H:%i') <= '23:59'
  • Вам нужно всего 10 результатов, но базе данных придется запускать подзапросы "имена" для каждого из результатов до LIMIT (что может быть много?). Поэтому я бы рекомендовал извлечь подзапрос из предложения SELECT во временную таблицу, проиндексировать его и присоединиться к нему (см. Исправленный запрос ниже).

Чтобы оптимизировать запрос, давайте начнем с добавления этих индексов:

ALTER TABLE 'classes' ADD INDEX 'classes_index_1' ('id', 'name');
ALTER TABLE 'students' ADD INDEX 'students_index_1' ('active', 'user_id', 'class', 'name', 'date');
ALTER TABLE 'users' ADD INDEX 'users_index_1' ('user_id', 'full_name');

Теперь создайте временную таблицу (первоначально это был подзапрос в предложении SELECT) и проиндексировали ее:

-- Transformed subquery to a temp table to improve performance
CREATE TEMPORARY TABLE IF NOT EXISTS temp1 AS SELECT
        count(*) AS names,
        name 
    FROM
        students AS inner_st 
    WHERE
        1 = 1 
    GROUP BY
        name 
    ORDER BY
        NULL

-- This index is required for optimal temp tables performance
ALTER TABLE
  'temp1'
ADD
  INDEX 'temp1_index_1' ('name', 'names');

И измененный запрос:

SELECT
        SQL_CALC_FOUND_ROWS st.id,
        st.date,
        st.image,
        st.user_id,
        st.status,
        ck.name AS class_name,
        users.full_name,
        temp1.names 
    FROM
        students AS st 
    LEFT JOIN
        users 
            ON st.user_id = users.user_id 
    LEFT JOIN
        classes AS ck 
            ON st.class = ck.id 
    LEFT JOIN
        temp1 
            ON st.name = temp1.name 
    WHERE
        st.date BETWEEN '2018-01-17' AND DATE_ADD('2018-01-17', INTERVAL 1 DAY) 
        AND st.active = 1 
    ORDER BY
        st.date DESC LIMIT 0,
        10
  • 0
    спасибо SQL_CALC_FOUND_ROWS это моя проблема. Как мне посчитать ряды конкретных фильтров. Это заняло у меня 2 секунды на 100 000 строк, мне нужен номер для нумерации страниц
  • 0
    @Guz - Я не уверен, что понял ваш вопрос. Можете ли вы уточнить? Вы запрашиваете альтернативу для SQL_CALC_FOUND_ROWS, потому что это замедляет ваш запрос?
Показать ещё 2 комментария
0

Попробуйте сначала:

INDEX(active, date)

Пользователь user_id PK для users? Является ли class_id PK для classes? Если нет, то они должны быть INDEXed.

Почему вы проверяете время раздельно?

Исправьте тест, так что очевидно, какая таблица находится в каждом столбце.

Вам действительно нужно LEFT JOIN? Или JOIN хватает? В последнем случае есть больше вариантов оптимизации.

Дайте некоторые реалистичные примеры других SELECTs; может потребоваться другой индекс (ы).

"Первая" страница медленная? Или только более поздние страницы? См. Это для оптимизации разбиения на страницы - не используя OFFSET.

  • 0
    user_id и class не PK. Просто идентификатор, который подходит для пользователей и таблицы классов. Я пытался индексы, которые вы написали. Все еще медленно. Я думаю, что я могу удалить соединение слева / слева отсюда, но таблица классов содержит 4 строки, а пользователи 2 строки
  • 0
    Я обновил вопрос, у меня есть другой внутренний запрос, когда я удалил его быстрее, как я могу проиндексировать это? (выберите count (*) для студентов как inner_st, где st.name = inner_st.name) в качестве имен,
Показать ещё 10 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню