За последние 24 часа нас посетили 36629 программистов и 1704 робота. Сейчас ищут 1036 программистов ...

Оптимизация запросов к БД при постраничном выводе

Тема в разделе "PHP для новичков", создана пользователем bocharsky, 31 май 2014.

  1. bocharsky

    bocharsky Новичок

    С нами с:
    18 апр 2014
    Сообщения:
    77
    Симпатии:
    1
    Добрый день.
    Подскажите плиз как оптимизировать работу с базой при постраничном выводе результатов поиска.

    Ситуация:
    Провожу поиск по базе, результаты вывожу постранично («пейджер»).
    Прочитал холивары про SQL_CALC_FOUND_ROWS vs SELECT COUNT (*). Общественность настаивает, что SQL_CALC_FOUND_ROWS для больших баз – гибельное дело, так как даже при наличии LIMIT все равно сканирует все записи. В то время как SELECT COUNT (*) гораздо быстрее, так как не сканирует строки, а как-то использует индекс или что-то типа того.

    Получается, мне надо дважды запускать поиск?
    1. SELECT COUNT (*) FROM table [УСЛОВИЕ ПОИСКА] – для получения количества записей, которое будет в выборке
    2. SELECT * FROM table [УСЛОВИЕ ПОИСКА] LIMIT start_row, row_per_pages - для построения постраничной выдачи
    Сдается мне что два раза проводить поиск – как-то неправильно.

    Также из доков следуюет, что:
    COUNT(*) is optimized to return very quickly if the SELECT retrieves from one table, no other columns are retrieved, and there is no WHERE clause.
    This optimization applies only to MyISAM tables only, because an exact row count is stored for this storage engine and can be accessed very quickly.
    http://dev.mysql.com/doc/refman/5.6/en/group-by-functions.h ... tion_count
    У меня таблицы MyISAM – ОК
    Но насколько я понимаю, если я использую не чистый SELECT COUNT(*) FROM table, а помещаю в запрос с COUNT свое «поисковое условие», то эта магическая быстрота уже не работает. Так?

    ВАРИАНТ Б
    Все гораздо проще и красивее, если использовать SQL_CALC_FOUND_ROWS:
    Тогда все делается в одном запросе:
    SELECT SQL_CALC_FOUND_ROWS * FROM table [УСЛОВИЕ ПОИСКА] LIMIT start_row, row_per_pages
    Тут получается, что сначала отбираются записи, соответствующие условию поиска, к ним применяются условия LIMIT, а SELECT SQL_CALC_FOUND_ROWS продолжает перебирать выборку, чтобы получить количество рядов.
    Но вот джедаи php использование SQL_CALC_FOUND_ROWS осуждают.

    Что еще приходит на ум:
    Может выбрать нужные ряды с помощью SELECT [УСЛОВИЕ ПОИСКА] и поместить в какую-то промежуточную сущность (временную таблицу, массив), сразу пересчитывая количество рядов, а затем уже из этой выборки выполнять «постраничный вывод»?

    Наверняка эта задачка уже решена джедаями php, буду рад советам.
    Спасибо!
     
  2. Zuldek

    Zuldek Старожил

    С нами с:
    13 май 2014
    Сообщения:
    2.381
    Симпатии:
    344
    Адрес:
    Лондон, Тисовая улица, дом 4, чулан под лестницей
    Во-первых, использование SQL_CALC_FOUND_ROWS и признание оптимальности не прерогатива пхпшеров.

    Во-вторых, если таблица по которой вы ищите имеет число записей менее 10000000, не думайте о разнице в скорости двух методов, используйте SQL_CALC_FOUND_ROWS.

    В третьих, универсально правильного ответа на ваш вопрос не существует. Можно привести примеры, где использование SQL_CALC_FOUND_ROWS будет значительно медленнее чем отдельный запрос с count(id) и примеры где всё с точности до наоборот. Если нет понимания того какая из альтернатив в конкретном случае будет уместнее то нужно тестировать свои запросы.

    При выборке данных в сложных запросах поиска по бд в современных проектах, правильном подходе при создании индексов, использовании в запросе нескольких джоинов и десятков параметров оператора WHERE, запрос с SQL_CALC_FOUND_ROWS будет выполнятся быстрее, чем указанная альтернатива с двумя запросами (как известно, запрос select found_rows() не касается извлечения данных).
     
  3. bocharsky

    bocharsky Новичок

    С нами с:
    18 апр 2014
    Сообщения:
    77
    Симпатии:
    1
    Большое спасибо!