За последние 24 часа нас посетили 17639 программистов и 1628 роботов. Сейчас ищет 1661 программист ...

тормозит COUNT(*), альтернатива?

Тема в разделе "PHP и базы данных", создана пользователем pioner7, 14 июл 2008.

  1. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    кто нибудь подскажет альтернативу COUNT(*), а то делаю выборку по таблице в которой более 1млн записей,
    сама выборка проходит меньше 0 сек, а вот COUNT(*) по тем же параметрам WHERE работает более 10 сек, а то более 20 сек. Мне надо сделать постраничный вывод, а для этого я так понимаю обязательно знать сколько всего записей попадающих под условие WHERE. Выборка идёт по LIMIT-у и с ORDER BY.

    EXPLAIN:
    id select_type table type possible_keys key key_len ref rows Extra
    1 SIMPLE main range PRIMARY PRIMARY 4 NULL 602188 Using where

    спасибо за внимание.
     
  2. Kreker

    Kreker Старожил

    С нами с:
    8 апр 2007
    Сообщения:
    5.433
    Симпатии:
    0
    А индекс в столбце, по которому проходит условие, стоит?
     
  3. Sergey89

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

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
  4. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    если бы не стоял, то врядли выборка из млн записей шла бы меньше 0 сек :)
    конечно стоит, все условия из WHERE построены строго по индексным полям одно индекса.
    Я вывожу только 10 стр из всей выборки, но навигацию надо сделать с учетом общего кол-ва из COUNT(*)
     
  5. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    вставил в select и получил выборку более 3 сек, а без него меньше 0,0 сек :(
     
  6. Psih

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

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Запрос в студию, это раз (и оформляйте вставки тегом [ code ] и SQL запросы [ sql ] тегом).
    Cтруктуру тоже в студию - два. У вас там что-то очень не хорошо.
     
  7. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    CREATE TABLE `main` (
    `datasort` int(11) unsigned NOT NULL default '1',
    `field1` mediumint(5) unsigned NOT NULL default '1',
    `data` int(8) unsigned NOT NULL default '1',
    `area` smallint(4) unsigned NOT NULL default '1',
    `field2` smallint(4) unsigned NOT NULL default '1',
    `field3` tinyint(1) unsigned NOT NULL default '1',
    `celm` tinyint(1) unsigned NOT NULL default '1',
    `cel1` tinyint(1) NOT NULL,
    `cel2` tinyint(1) NOT NULL,
    `cel3` tinyint(1) NOT NULL,
    `cel4` tinyint(1) NOT NULL,
    `kol` smallint(3) NOT NULL,
    `id` int(11) unsigned NOT NULL default '1',
    PRIMARY KEY (`datasort`,`field1`,`data`,`area`,`field2`,`feild3`,`celm`,`cel1`,`cel2`,`cel3`,`cel4`),
    KEY `id` (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    [SQL]
    SELECT `id`
    FROM `main`
    WHERE `datasort` > 0
    AND `field1` = 7
    AND `data` > '19601010'
    AND `data` < '19951010'
    AND `area` > 0
    AND `field2` > 0
    AND `field3` > 0
    AND `celm` = 1
    AND `cel1` > 1
    AND `cel2` >=0
    AND `cel3` >=0
    AND `cel4` >=0
    AND `kolfoto` > 0
    ORDER BY `datasort` DESC
    LIMIT 0,10
    [SQL]
    Показывает записи 0 - 9 (10 всего, Запрос занял 0.6881 сек)
    с COUNT(*) 7 секунд
     
  8. Anonymous

    Anonymous Guest

    pioner7,
    ?
     
  9. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    насколько я понял необходимо переделать индекс на простой INDEX? а поле `ID` можно поставить первым и сделать его PRIMARY INDEX?
     
  10. Dagdamor

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

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    pioner7
    Если у тебя в условии идет не жесткое сравнение (datasort = 12345), а более мягкое (datasort > 0), то пихать несколько полей в индекс не имеет смысла - это дополнительные поля все равно невозможно использовать для оптимизации выборки.

    Кроме того, попробуй запрос не с LIMIT 0,10, а с LIMIT 500000,10. ;) Думается мне, что скорость будет сравнима с COUNT'ом.
     
  11. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    datasort я ставлю на первое место потому что это поле у меня в ORDER BY и насколько я заметил опытным путем (я его перемещал в разные места) если поле из ORDER BY не первое в индексе то EXPLAIN показывает "file sort". Насколько я понимаю это плохо, а так только "Using where", поэтому мне все равно какое значение в datasort > 0. Может я ошибаюсь с order by, поправьте.
     
  12. Dagdamor

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

    С нами с:
    4 фев 2006
    Сообщения:
    2.095
    Симпатии:
    1
    Адрес:
    Барнаул
    pioner7
    Ты все правильно говоришь, datasort должен быть первым в индексе, раз уж по нему ты сортируешь, я лишь хотел сказать, что смысла цеплять к индексу дополнительные поля в твоем случае нет. По крайней мере индекс будет меньше места занимать, и операции добавления/удаления будут быстрее.
     
  13. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    2-3 сек
     
  14. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    order by из запроса с COUNT(*) удалить. и наверняка еще можно упростить.
     
  15. MiksIr

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

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Обоснуйте, пожалуйста.
     
  16. MiksIr

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

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Это вообще лирика. Если я правильно перевел, тут говориться, что типа если хотите мульти-праймари кей, пишите его отдельно, ибо указывая праймари кей как свойство поля сделать мультипл кей нельзя.
     
  17. MiksIr

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

    С нами с:
    29 ноя 2006
    Сообщения:
    2.339
    Симпатии:
    44
    Зы: а можно эксплейны в студию...
    - обычный запрос
    - обычный запрос без лимита
    - запрос с каунтом
    полностью, со временем исполнения ;)
    Ну и просто запрос с каунтом... сколько строк и время
     
  18. EugeneTM

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

    С нами с:
    19 апр 2008
    Сообщения:
    85
    Симпатии:
    0
    Первое что тебе нужно сделать - это залезть в мануал и прочитать, а еще лучше ПОНЯТЬ как используются индексы, когда используются индексы и как нужно строить запросы.

    Блин, ТАКОГО как
    я еще нигде не видел.

    Первое. Оптимизатор начинает использовать индексы ТОЛЬКО если по его предварительной оценке, количество записей в результате будет не более 30% от общего количества записей в таблице. Иначе, он абсолютно справедливо считает, что быстрее будет тупо перелопатить ВСЕ записи таблицы не используя индексы. Ну и сколько записей вернет условие `datasort` > 0 ? По твоему эксплайну оптимизатор считает 602188. Можешь тупо удалить любые индексы, скорость выполнения запроса будет та же.

    Второе. Посмотри на порядок условий в WHERE. Почитай мануал как он и на что влияет.
    Третье ...
    ....
    Восьмое ...

    И главное. Первое условие выбора в запросе ДОЛЖНО возвращать минимальное количество записей. Думаю понятно зачем.

    Почитай как работает COUNT и LIMIT.
    И никогда больше не пиши
    Тебе тут выше абсолютно справедливо заметили
    И т.д. и т.п.
     
  19. EugeneTM

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

    С нами с:
    19 апр 2008
    Сообщения:
    85
    Симпатии:
    0
    И т.д. и т.п.[/quote]
    Был не совсем прав.
    Не имеет смысла в случае
     
  20. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    [sql]
    EXPLAIN SELECT COUNT( * )
    FROM `main`
    WHERE `datasort` >0
    AND `f1` = '1'
    AND `data` <= '19730807'
    AND `data` >= '19240807'
    AND `f2` >0
    AND `f3` >0
    AND `celm` =1
    AND `cel1` >1
    AND `cel2` > -1
    AND `cel3` > -1
    AND `cel4` > -1
    AND `f4` >0
    AND `kolfoto` > -1
    [/sql]
    Результат:
    id select_type table type possible_keys key key_len ref rows Extra
    1 SIMPLE main ALL datasort NULL NULL NULL 1002813 Using where

    [sql]
    EXPLAIN SELECT `f1` , `data` , `f2` , `cel1` , `cel2` , `kolfoto` , `id` , (

    SELECT `name`
    FROM `uname`
    WHERE `id` = `main`.`id`
    LIMIT 1
    )
    FROM `main`
    WHERE `datasort` >0
    AND `f1` = '1'
    AND `data` <= '19730807'
    AND `data` >= '19240807'
    AND `f2` >0
    AND `f3` >0
    AND `celm` =1
    AND `cel1` >1
    AND `cel2` > -1
    AND `cel3` > -1
    AND `cel4` > -1
    AND `f4` >0
    AND `kolfoto` > -1
    ORDER BY `datasort` DESC
    LIMIT 0 , 10
    [/sql]
    Результат EXPLAIN
    id select_type table type possible_keys key key_len ref rows | Extra
    1 PRIMARY main range datasort datasort 4 NULL 338498 | Using where
    2 DEPENDENT SUBQUERY uname eq_ref PRIMARY PRIMARY 4 myworld.main.id | 1

    Далее вставил в скрипт перед выборками и после функцию echo microtime и вот результаты:
    Запрос 1
    Выбрало 557 записей:
    перед выборками microtime = 0.59375500 1218115664
    сразу после COUNT(*) и перед обычной выборкой с LIMIT 0,10 = 0.70165500 1218115667
    после обычной выборки = 0.09092200 1218115668

    Запрос 2
    Выбрало 3885 записей:
    перед выборками microtime = 0.57813000 1218116665
    сразу после COUNT(*) и перед обычной выборкой с LIMIT 0,10= 0.78304600 1218116668
    после обычной выборки = 0.16738900 1218116669

    Запрос 3
    Выбрало 1 запись:
    перед выборками microtime = 0.15625500 1218116859
    сразу после COUNT(*) и перед обычной выборкой с LIMIT 0,10 = 0.16158500 1218116862
    после обычной выборки = 0.54541500 1218116872

    Запрос 4
    Выбрало 4583 записей:
    перед выборками microtime = 0.98437900 1218117017
    сразу после COUNT(*) и перед обычной выборкой с LIMIT 0,10 = 0.11133700 1218117021
    после обычной выборки = 0.49315100 1218117021

    Запрос такой же как и 4 только с LIMIT 3000, 10
    Выбрало 4583 записей:
    перед выборками microtime = 0.75000400 1218117453
    сразу после COUNT(*) и перед обычной выборкой с LIMIT = 0.88853300 1218117456
    после обычной выборки = 0.85412600 1218117462
     
  21. EugeneTM

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

    С нами с:
    19 апр 2008
    Сообщения:
    85
    Симпатии:
    0
    Это непобедимо
    Мож расстреливать тех кто мануал читать не хочет.

    quote from manual

    Во всех остальных случаях перебираются ВСЕ записи.

    Забудь лимит.
    Читай мануал
    5.2 Оптимизация SELECT и других запросов
     
  22. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    ну ты интересный, лекго ляпнуть не используй LIMIT, а как на практике выбрать только 10 записей без LIMIT?
    а как делать навигацию без LIMIT 100,10 ? может подскажешь?
    Да ещё, если ты внимательно прочитаешь заголовок, то увидешь что у меня нет претензий к LIMIT, а только к COUNT(*).
     
  23. EugeneTM

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

    С нами с:
    19 апр 2008
    Сообщения:
    85
    Симпатии:
    0

    Задаешь вопрос
    Тебя посылают на мануал, хотя наверное стоило бы подальше.
    Там написано как работает COUNT
    Там написано ЧТО нужно делать что бы было быстрее
    В твоем эксплайне черным по белому написано где у тебя тормоза

    И насрать на то сколько времени у тебя выполняется запрос с LIMIT'ом
    COUNT от этого быстрее работать не будет
    А точное количество записей ты по другому не получишь.

    А если ты не только мануал не прочитал , но и то что в топике написано
    Выбирает у тебя по 500-5000 записей, а в эксплайне
    А теперь попробуй извилины напрячь и понять , что у тебя не так.
     
  24. pioner7

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

    С нами с:
    28 ноя 2007
    Сообщения:
    54
    Симпатии:
    0
    тогда предложи вариант, как сделать ORDER BY `datasort` так что бы EXPLAIN не показывал Use filesort?
    я знаю что, это у меня узкое место.
     
  25. EugeneTM

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

    С нами с:
    19 апр 2008
    Сообщения:
    85
    Симпатии:
    0
    А попробуй ответить СЕБЕ на вопрос.
    Зачем тебе сортировать записи для получения COUNT ?