За последние 24 часа нас посетил 26461 программист и 1814 роботов. Сейчас ищут 1006 программистов ...

Помогите ...: проблемы с COUNT() после GROUP BY (closed)

Тема в разделе "MySQL", создана пользователем mazyakov, 10 авг 2009.

  1. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    Помогите переписать или исправить запрос. Есть три таблицы: 1-ая это категории, 2-ая ссылки, 3-я рейтинги ссылок. Нужно сделать выборку всех полей ссылок для определенной категории, а так же подсчитать среднее значение для рейтингов и отсортировать по рейтингам и дате создания ссылок. Запрос такой и он замечательно работает:
    [sql]
    SELECT
    `main_table`.*,
    `cat`.*,
    AVG(dlr_rating) AS `avg_rating`
    FROM `dx_linkexchange_links` AS `main_table`
    LEFT JOIN `dx_linkexchange_category` AS `cat` ON main_table.dll_dlc_id = cat.dlc_id
    LEFT JOIN `dx_linkexchange_rate` AS `rating` ON main_table.dll_id = rating.dlr_dll_id
    WHERE
    (cat.store_id = '1')
    AND (main_table.dll_dlc_id = '3')
    AND (main_table.dll_status = 2)
    GROUP BY `dll_id`
    ORDER BY
    `avg_rating` DESC,
    `dll_created_time` ASC;
    [/sql]
    После чего система сама (пишу на готовом движке) удаляет все выбираемые поля в запросе, подставляя COUNT(*), удаляет сортировку и лимит. Метод класса я изменить или переопределить не могу. Получается следующее:
    [sql]
    SELECT COUNT(*)
    FROM `dx_linkexchange_links` AS `main_table`
    LEFT JOIN `dx_linkexchange_category` AS `cat` ON main_table.dll_dlc_id = cat.dlc_id
    LEFT JOIN `dx_linkexchange_rate` AS `rat` ON main_table.dll_id = rat.dlr_dll_id
    WHERE
    (cat.store_id = 1)
    AND (main_table.dll_dlc_id = 3)
    AND (main_table.dll_status = 2)
    GROUP BY `dll_id`;
    [/sql]
    Запрос в данном случае возвращает 13 строк, а не число 13.
    Задача в том, что бы написать запрос, который вернул бы нужные поля для вывода, и после обработки (см.выше) вернул правильное число полей. Вот. Заранее всех благодарю.
     
  2. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    Подскажите ещё, почему может не работать COUNT(), если сделать вложенный запрос типа:
    [sql]
    SELECT COUNT(*)
    FROM
    (SELECT
    `main_table`.*,
    `cat`.*,
    AVG(dlr_rating) AS `avg_rating`
    FROM `dx_linkexchange_links` AS `main_table`
    LEFT JOIN `dx_linkexchange_category` AS `cat` ON main_table.dll_dlc_id = cat.dlc_id
    LEFT JOIN `dx_linkexchange_rate` AS `rating` ON main_table.dll_id = rating.dlr_dll_id
    WHERE
    (cat.store_id = 1)
    AND (main_table.dll_dlc_id = 3)
    AND (main_table.dll_status = 2)
    GROUP BY `dll_id`) AS `tbl`
    [/sql]
    Возвращает 0.
     
  3. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    Всем спасибо, разобрался сам. Заработало так:
    [sql]
    SELECT COUNT(*) FROM (
    SELECT
    `main_table`.*,
    `cat`.*,
    AVG(dlr_rating) AS `avg_rating`
    FROM
    `dx_linkexchange_links` AS `main_table`
    LEFT JOIN `dx_linkexchange_category` AS `cat` ON main_table.dll_dlc_id = cat.dlc_id
    LEFT JOIN `dx_linkexchange_rate` AS `rating` ON main_table.dll_id = rating.dlr_dll_id
    WHERE
    (cat.store_id = '1')
    AND (main_table.dll_dlc_id = '3')
    AND (main_table.dll_status = 2)
    GROUP BY `dll_id`
    ORDER BY
    `avg_rating` DESC,
    `dll_created_time` ASC) As `tbl`;[/sql]
    Т.е. при наличии сортировки. Вот.
    Если кто объяснит почему, буду очень благодарен.
     
  4. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    разбираться лениво. пока вопрос - зачем во вложенном запросе выбираются все поля, да еще не от одной таблицы?
     
  5. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    armadillo
    К сожалению вся проблема в том, что данный запрос формируется посредством коллекции условий модели (кто работал на платформе Magento, тот должен быть в курсе), и для вывода списка с постраничной навигацией, необходим весь запрос целиком, т.е. он один на всё. В этом вся проблема. Но вы правы, абсолютно все данные мне не нужны, но это всего дополнительно три поля. Тогда следует вопрос, на сколько это ускорит выполнение запроса, если указать только необходимый перечень полей?
     
  6. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    Да, действительно проверил опытным путём.
    Запрос с объединением выполнялся 94 ms - стал 62 ms.
    При подсчёте общего количества с вложенным запросом, был 62 ms - стал 15 ms
    Это действительно очень сильно ускорило выполнение скрипта.
    armadillo спасибо за совет.

    Я считаю, что вопрос можно закрывать. Спасибо всем, кто посмотрел и поучаствовал. )
     
  7. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    конкретно для count можно и сам запрос заметно упростить.
    [sql]SELECT COUNT(distinct `dll_id`)
    FROM
    `dx_linkexchange_links` AS `main_table`
    JOIN `dx_linkexchange_category` AS `cat` ON main_table.dll_dlc_id = cat.dlc_id AND cat.store_id = '1'
    WHERE
    main_table.dll_dlc_id = '3'
    AND main_table.dll_status = 2[/sql]

    (LEFT для этого JOIN'а не нужен)
    у меня в пейджере предусмотрена возможность отдельного запроса именно поэтому.
    Такой запрос наверняка будет побыстрее, тем более чем сортировка по разным правилам.

    Что "оно работает при добавлении сортироки" - большие сомнения, не надо шаманить.


    Как оно организованно в твоем случае - я не вижу, ты читал это?
     
  8. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    Ссылку, которую вы мне дали, я уже посмотрел. Но я работаю не на своей платформе. Система организованна на движке Magento (eCommerce Platform). Сам Magento использует Zend Framework. Для вывода списка с постраничной навигацией для frontend-a используется уже готовый блок (объект) (использование уже готовых (встроенных) решений - требование заказчика), который берет "коллекшен" (набор правил в виде методов объекта модели) и возвращает уже готовый результат. Это очень коротко. :) Если говорить русским языком - чистого SQL-я там нет и, если делать всё правильно, не должно быть. Всё через объекты. Только вот иногда случаются такие вот проблемки.
     
  9. Mr.M.I.T.

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

    С нами с:
    28 янв 2008
    Сообщения:
    4.586
    Симпатии:
    1
    Адрес:
    у тебя канфетка?
    понаделают кривых Sql билдеров
     
  10. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    запрос на общее кол-во у тебя все равно отдельный. Как выглядят правила?
     
  11. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    armadillo
    Да, вы правы, его можно сделать отдельно не используя текущий "колекшен", но это не совсем правильно. Если делать правильно, то я вообще не должен заморачиваться по поводу подсчёта, система сама должна правильно подсчитать. Точно подметил Mr.M.I.T. по поводу кривых SQL билдеров. Я могу привести цепочку логики, просто это уже не будет относиться к этой ветке форума:

    "Колекшен" в блоке:
    PHP:
    1. $collection = Mage::getResourceSingleton('linkexchange/links_collection')
    2. ->joinRating()
    3. ->addCategoryFilter($categoryId)
    4. ->addStatusFilter()
    5. ->addSortOrder();
    6. $collection->addCount();
    7. $this->setItems($collection);
    Вот из этого формируется SQL (кроме последней строки).

    Формируем блок для вывода пагинатора:
    PHP:
    1. $pager = $this->getLayout()->createBlock('page/html_pager', 'links.pager');
    2. $pager->setCollection($this->getItems());
    3. $this->setChild('pager', $pager);
    4. $this->getItems()->load();
    Если описывать все правила, формирующее "колекшен" будет ещё 6 методов. Если желаете, могу и их бросить в ветку.
     
  12. mazyakov

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

    С нами с:
    29 май 2006
    Сообщения:
    84
    Симпатии:
    0
    Адрес:
    Minsk
    armadillo
    При написании отдельного запроса в модели, необходимо будет еще передавать туда ряд параметров, т.е. повторять уже реализованный на более низком уровне функционал. Это не совсем хорошо.
    Вы знаете, я проверил разницу в производительности между нашими решениями, она очень незначительна. Поэтому я решил ничего не менять.
     
  13. armadillo

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

    С нами с:
    6 апр 2007
    Сообщения:
    2.380
    Симпатии:
    0
    Адрес:
    Russia, Moscow
    Разница будет зависеть от кол-ва записей. но поскольку я так и не выяснил, куда ты вставляешь запрос и как он делается без этого, ничего сказать не могу.