За последние 24 часа нас посетили 34092 программиста и 1822 робота. Сейчас ищут 858 программистов ...

COUNT vs SQL_CALC_FOUND_ROWS

Тема в разделе "Прочее", создана пользователем mpak, 30 июл 2010.

Статус темы:
Закрыта.
  1. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    глянь, возможно пригодится :)

    http://www.php.ru/forum/viewtopic.php?p=226643#226643

    случайно подглядел идею в оскоммерце, но ее тамошняя реализация конечно крамешный капец как и весь оск в принципе)
     
  2. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    PHP:
    1. <?php
    2. public function buildCountSql($key = '*')
    3.       {
    4.           $sql = $this->sqlTpl;
    5.  
    6.           if(preg_match("#select.*?(distinctrow|distinct).*?from#ism", $sql, $distinct))
    7.           {
    8.               $key = strtoupper($distinct[1])." ".$this->db->escape($key);
    9.           }
    10.           else
    11.           {
    12.               $key = $this->db->escape($key);
    13.           }
    14.  
    15.           $countSql = "SELECT COUNT(".$key.") `count` ";
    16.  
    17.           $regex = "(?:\sHAVING\s|\sORDER\s|\sLIMIT\s|\sPROCEDURE\s|\sINTO\s|\sFOR\s+UPDATE\s|$)";
    18.           preg_match("#(FROM.*?)$regex#ism", $sql, $sqlInner);
    19.           $countSql .= $sqlInner[1];
    20.  
    21.           return $countSql;
    22.       }
     
  3. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    phpdude
    Ага, а потом у тебя есть запросы с JOIN. В одном случае COUNT(*) нужно делать только по одной таблице без JOIN, в другой с JOIN, в третем у тебя FROM table1 JOIN table2 - а COUNT(*) FROM table2 идёт. А если оставлять лишние JOIN, то эффект от COUNT(*) сильно уменьшается, а то и ещё хуже чем от SQL_CALC_FOUND_ROWS.
    Так что не вариант, тут всё гораздо тоньше :)
     
  4. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    Psih
    ну в этой ситуации да)) универсальное решение тяжелее найти)) я про среднячковые запросы имел ввиду), жойн то она хавает)) новоть исключить какой нить жойн это да :)
     
  5. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    phpdude
    Исключение JOIN'а часто имеет разницу в скорости работы такую, что тогда можно и SQL_CALC_FOUND_ROWS использовать если оставить JOIN.
     
  6. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    самый крутой подход, это когда ты не делаешь каунтов всяких и всё. Закончился фетч, не результатов запроса - вот тебе и всё как бы... ;)
     
  7. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    Psih
    уху, я знаю)) просто хотел помочь :D
     
  8. mpak

    mpak Активный пользователь

    С нами с:
    30 окт 2006
    Сообщения:
    440
    Симпатии:
    0
    Пример приведи.
     
  9. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    читай документацию
     
  10. mpak

    mpak Активный пользователь

    С нами с:
    30 окт 2006
    Сообщения:
    440
    Симпатии:
    0
    Напишите большими буквами над форумом "Читайте документацию" и закройте форум.
    Если кроме этой фразы сказать нечего.
    Я тебе не с документации разве приводил примеры?

    Нашел такое.

    Получается все опять же просто. Не желательно применять данную конструкцию для UNION. Но для просых запросов без UNION в работе ведет себя адекватно. То есть применение его в простых запросах обосновано. К тому же еще и быстрее работает.

    Считаем вопрос исчерпан? А применять ли данную конструкцию в коде или нет пусть каждый решает для себя. Для меня она достаточно удобная. Если знать в каких случаях лучше не применять а здесь хорошо этот случай описан то можно использвать без проблем. Использую ее давно и от вас только услышал что она работает где то некорректно.
     
  11. Mr.M.I.T.

    Mr.M.I.T. Старожил

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    даа, это такой соблазн =)
     
  12. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    mpak
    чел, пиши свой говнокод, и не парь людям мозг, которые знают больше тебя, поверь, больше. Ты ничего не докажешь, и не надо гнать на форум по поводу того что ТЫ зелёный.
     
  13. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    mpak
    Ну как хотите, раз вы не способны сами довести дело до конца, не умеете или не хотите помучать гугль и не видите дальше своего носа, то я назову вас троллем, теоретиком, буквоедом и ещё кучей страшных обидных слов. У вас за спиной голая теория из мануала и ничего более. В реальности есть масса обстоятельств, которые всё меняют. К тому же часто документация не упоминает о подводных камнях или особенностях работы, потому что они всплывают только на практике и при некоторых условиях (в данном случае это незнание механики работы SQL_CALC_FOUND_ROWS + объемные таблицы).

    Начните с этого: http://www.mysqlperformanceblog.com/200 ... ound_rows/ - это блог основателей и авторов самого MySQL в том виде, в котором вы его сегодня используете. Данный блог один из самых точных и наиболее полных по внутренностям MySQL как таковым. Это что бы не быть голословным.

    А теперь немного логики и знания как работает MySQL изнутри:

    У вас есть запрос: SELECT field1, field2 FROM table WHERE field3 = 25 LIMIT 0, 30. Таблица состоит из 3-х полей типа int по 4 байта каждый
    По field3 у нас есть индекс.
    PRIMARY KEY индекс не учитываю, это ещё 4 байта умноженное на кол-во записей.

    Берём ситуацию с 1000 строчек. Данные весят 4 * 3 * 1000 + индекс 4 * 1000 = итого 16000 байт + внутренние расходы. При таком объёме данных они легко умещаются в RAM и их так мало, что можно даже не ставить индекс - всёравно всё будет работать моментально.

    Увеличим до 100 000 записей. Данных у нас на 4 * 3 * 100 000 ~ 1.17 MB + индекс. В общем-то тоже легко лезет в память и очень быстро фильтруется, хотя индекс уже даст о себе знать. Что COUNT(*), что SQL_CALC_FOUND_ROWS - особой разницы не будет.

    Давайте увеличим радикально объём данных и кол-во строчек. Возмём сразу 10 000 000, что кстати для соц. сети не много (к примеру оценки фоток за пол года набежало на 40 000 000 записей) - это уже ~150+ MB с данными и индексом. Более-менее объёмчик есть. В память влезет, но кол-во строк, которые надо перебрать будет занимать более-менее весомое время. Вот тут и начинается веселье и сказывается та самая разница, как работает COUNT и SQL_CALC_FOUND_ROWS.

    COUNT(*) для innodb просто считает сколько записей удовлетворяет условию. При этом основной запрос в данном случае работает так: берётся индекс и по нему отбираются записи. Как только набираем 30 штук - останавливаемся и возвращаем результат. Если ещё и ORDER BY по индексу, так вообще всё очень шустро, т.к. индекс сам по себе сортирован всегда. В результате выбрали 30 записей, потом сделали COUNT(*) который по индексу быстро подсчитал сколько там примерно записей и вернул результат.

    В случае с SQL_CALC_FOUND_ROWS всё работает уже совсем по другому. SQL_CALC_FOUND_ROWS говорит в запросе буквально следующее: отдай мне на вывод 30 записей, но посчитай сколько их всего есть. Это ВАЖНЫЙ момент. Выборка работает так-же по индексу, но выбирает не 30 записей, а ВСЕ которые удовлетворяют WHERE, т.е. происходит full index/table scan в зависимости от есть/нету индекса. Т.е. если по запросу из 40 миллионов записей соотвествует хотя-бы миллион, то весь этот миллион выбирается в буффер, сортируется, обрабатывается, записывается сколько в нём было записей и от него отсекается 30 нужных записей для отправки результата к клиенту. Чувствуете разницу между выборкой в буфер 30 записей и миллионом? Кол-во процессорного времени и памяти, используемое во втором случае в сотни и даже тысячи раз больше чем в первом случае. У вас даже может просто не хватить памяти при сложных запросах с JOIN и всё это полезет на хард в виде temporary table, что ещё сильнее всё усугубит.
    Апогеем будет когда вы пару вот таких многомиллионных таблиц свяжете через JOIN и влепите туда SQL_CALC_FOUND_ROWS. Поздравляю с запросами, которые работают по 2-5 секунд даже на очень мощьных серверах.

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

    • * Поисковые запросы, где поиск часто идёт по полям, на которых нет индексов (20-30 индексов не сделаешь - база просто загнётся на UPDATE/INSERT/DELETE)
      * Сложные JOIN запросы, где в WHERE условиях фигурируют поля из JOIN'ed таблиц (и то не всегда)
    В остальных случаях COUNT(*) работает быстрее, частенько в десятки раз и с ростом объёма таблиц разница только растёт. Особенно это видно, когда запрос у вас типа такого?
    [sql]
    SELECT ....
    FROM t1
    JOIN t2 ON t1.id = t2.t1_id
    JOIN t3 ON t1.id = t3.t1_id
    WHERE t1.status = "active" ORDER BY t1.added DESC
    LIMIT 0, 30
    [/sql]
    По status и added ставим естественно индексы. В таком случае добавление SQL_CALC_FOUND_ROWS смерти подобно, особенно когда у вас данные быстро копятся.
    [sql]SELECT COUNT(*) FROM t1 WHERE t1.status = "active"[/sql]
    выполнится в купе с основным запросом быстрее, чем один с SQL_CALC_FOUND_ROWS гарантировано. Смотрите выше почему ещё раз, если не понятно.
     
  14. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    зачем вобще тогда нужен калк? =(

    в случае с сорокамиллионной базой запрос вернет тридцать записей, но калк выдаст число миллион?
     
  15. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    igordata
    Именно так и будет. И если COUNT(*) делает просто счётчик и не жрёт память, то CALC сделает temporary table со всеми вытекающими.
    А зачем он нужен я описал - запросы со сложными условиями (а особенно когда не на все поля есть индексы - тогда без него вообще никуда), в некоторых случаях с GROUP BY и прочие условия, которые в WEB реально встречаются не часто и в основном в проектах среднего и большого масштаба в единичных случаях на фоне общего кол-ва SQL запросов.
    А вот к примеру всякие корпоративные системы и прочий специализированный софт как раз может потребовать серьёзно думать где использовать COUNT(*), а где CALC_FOUND_ROWS
     
  16. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    Psih
    та что ты ему объясняешь, чел реально думает, что он Бог программирования...

    udp: ну вы поняли, кому именно объясняешь..
     
  17. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Костян
    Плевать, зато всю злость выпустил. А вдруг работодатель его будущий/текущий прочитает топик на форуме и даст ему хорошего пинка из реальности? :D
     
  18. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    Psih
    Ну дай Бог. Мне нравиться когда люди начинают понимать и принимать свои ошибки.
     
  19. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    да нифига он не поймет, пока не завалит пару серверов своими запросам ии не получит за них пизды
     
  20. engager

    engager Активный пользователь

    С нами с:
    21 янв 2009
    Сообщения:
    1.106
    Симпатии:
    1
    да.. дружелюбненько тут у вас.
    зачем обсер то сразу устраивать? нормальная дискуссия получилась.
    я бы даже перенес в подфорум по оптимизации/производительности/итд
     
  21. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    engager
    Да потому что это уже такая избитая тема, по которой столько информации в интернете, что только полный идиот будет спорить против.
     
  22. mpak

    mpak Активный пользователь

    С нами с:
    30 окт 2006
    Сообщения:
    440
    Симпатии:
    0
    Господа. Опять же хочу проверить все практическим путем. Есть таблица чуть меньше двух миллионов записей.

    SELECT * FROM mp_facebook_feed LIMIT 10
    Время исполнения: 0.000181 c.

    SELECT COUNT(*) FROM mp_facebook_feed
    Время исполнения: 0.000104 c.

    COUNT(*)
    1620373

    Если верить всему вышесказанноу то CALC_FOUND_ROWS должен ужасно тормозить при током количестве записей так как если верить вышесказанному должен выбрать все записи из базы. Проверяем.

    SELECT SQL_CALC_FOUND_ROWS * FROM mp_facebook_feed LIMIT 10
    Время исполнения: 0.000194 c.

    C учетом того что помимо количества записей из базы еще и вернулось какое то количество строк то это вполне достойный результат. Судя по скорости выборки я могу сделать вывод что алгоритм работы не тот что описал Psih. Хорошо видно что данный запрос все записи из таблицы не возвращаются.

    Вот как раз поэтому и нужно сомневаться во всем. Начитаются всякого говна пятригодичной давности. И живут в своем мирке. Думают что самые умные. Допускаю что в работе SQL_CALC_FOUND_ROWS на каком то этапе разработки mysql и были проблемы в работе. Но их уже давно пофиксили а вы друг другу так гавно в уши и льете что это шаманство оно глючит и тд. А проверить никто не может. Ссылку за все время обсуждения, какой то другой источник информации я только одну увидел. Пример плохо медленного или глючного запроса я так и не увидел. Хотя привел уже кучу ссылок и кучу примеров скорости работы. Обсуждение превращается в подобие.

    - Я лучше всех знаю мне даже в мануал не надо смотреть бла бла бла
    - Да вы все уроды я лучше знаю и проверять не буду я бог бла бла бла
    - Какие примеры в жопу все примеры я и сам знаю вы уроды я лучше всех бла бла бла

    ПРоверяйте то что пишите. И ищине информацию. То о чем вы говорите уже давным давно не актуально. Запрос хорошо работает при больших таблицах. На UNION только не проверял но и это думаю сделаю скоро.

    Запрос при котором данная конструкция работает некорректно или долго мне не смог привести никто. Во всех запросах которые я проверял SQL_CALC_FOUND_ROWS работает быстрее. Плюс другие плюсы этой конструкции.

    Некоторое время назад я спросил у одного человека о его мнении об mysql и он сказал что это гавно. Выяснилось что у него был опыт использования mysql лет пять назад на версии три какой то. В итоге он сделал выводы что это гавно и так и считает до сих пор. Так вот те кто меня убеждает что запрос не работает (хотя все тесты показывают обратное) похожи на этого человека. Я когда то пробовал и поэтому это так. А проверять как то не хочется.
     
  23. Костян

    Костян Активный пользователь

    С нами с:
    12 ноя 2009
    Сообщения:
    1.724
    Симпатии:
    1
    Адрес:
    адуктО
    mpak
    используй SQL_CALC_FOUND_ROWS, он быстрее. Ты всё равно никого не переубедишь в их знаниях.
     
  24. mpak

    mpak Активный пользователь

    С нами с:
    30 окт 2006
    Сообщения:
    440
    Симпатии:
    0
    +5
     
  25. phpdude

    phpdude Активный пользователь

    С нами с:
    9 июл 2010
    Сообщения:
    697
    Симпатии:
    0
    ты кроме myisam табличек слышал что нить? ты знаешь чем они плохи? ты знаешь почем используют innodb? тесты у тебя опять сферические в вакууме, на них смотреть не интереснее чем ногти грызть на ногах ... хотя мне кажется что даже ногти грызть на ногах интереснее, имхо конечно.
     
Статус темы:
Закрыта.