ускорение метода count

Общие вопросы по использованию фреймворка. Если не знаете как что-то сделать и это про Yii, вам сюда.
Ответить
maxtorchel
Сообщения: 34
Зарегистрирован: 2013.11.19, 16:51

ускорение метода count

Сообщение maxtorchel »

когда открывается грид, фрэймворк вызывает метод count в CActiveFinder. Также есть метод createCountCommand в CDbCommandBuilder. Там для подсчета выполняется такой запрос "SELECT COUNT(*) FROM ($sql) sq" где $sql подзапрос из метода search модели.
Если подзапрос достаточно сложный, с джойнами и таблица большая, то он может выполняться неоправданно долго, у меня в одном из модулей, например, полторы секунды. Я решил попробовать изменить выбираемые поля (между select и from) на 1, ведь для каунта нам ничего не нужно. При этом запрос выполнялся менее полусекунды, тоесть в 3 раза быстрее.
Почему в стандартном фрэймворке не парсят подобным образом строку, ведь это существенно ускоряет работу с большими таблицами?
maxtorchel
Сообщения: 34
Зарегистрирован: 2013.11.19, 16:51

Re: ускорение метода count

Сообщение maxtorchel »

до

Код: Выделить всё

mysql> SELECT COUNT(*) 
    -> FROM (
    ->        SELECT `t`.`id` AS `t0_c0`, 
    ->          `t`.`name` AS `t0_c1`, 
    ->          `t`.`job_id` AS `t0_c2`, 
    ->          `t`.`request_id` AS `t0_c3`, 
    ->          `t`.`sender_id` AS `t0_c4`, 
    ->          `t`.`receiver_id` AS `t0_c5`, 
    ->          `t`.`sender_contact_id` AS `t0_c6`, 
    ->          `t`.`receiver_contact_id` AS `t0_c7`, 
    ->          `t`.`sender_address_id` AS `t0_c8`, 
    ->          `t`.`receiver_address_id` AS `t0_c9`, 
    ->          `t`.`pieces` AS `t0_c10`, 
    ->          `t`.`weight` AS `t0_c11`, 
    ->          `t`.`pieces_weight` AS `t0_c12`, 
    ->          `t`.`volume` AS `t0_c13`, 
    ->          `t`.`control_volume` AS `t0_c14`, 
    ->          `t`.`volume_weight` AS `t0_c15`, 
    ->          `t`.`volume_weight_divisor` AS `t0_c16`, 
    ->          `t`.`control_weight` AS `t0_c17`, 
    ->          `t`.`pieces_control_weight` AS `t0_c18`, 
    ->          `t`.`control_volume_weight` AS `t0_c19`, 
    ->          `t`.`size` AS `t0_c20`, 
    ->          `t`.`control_size` AS `t0_c21`, 
    ->          `t`.`date_expected_start` AS `t0_c22`, 
    ->          `t`.`date_expected_end` AS `t0_c23`, 
    ->          `t`.`time_expected_start` AS `t0_c24`, 
    ->          `t`.`time_expected_end` AS `t0_c25`, 
    ->          `t`.`date_received` AS `t0_c26`, 
    ->          `t`.`time_received` AS `t0_c27`, 
    ->          `t`.`receiver_lastname` AS `t0_c28`, 
    ->          `t`.`seal` AS `t0_c29`, 
    ->          `t`.`payer_id` AS `t0_c30`, 
    ->          `t`.`date_collect` AS `t0_c31`, 
    ->          `t`.`time_collect` AS `t0_c32`, 
    ->          `t`.`content_type_id` AS `t0_c33`, 
    ->          `t`.`desc` AS `t0_c34`, 
    ->          `t`.`package_type_ids` AS `t0_c35`, 
    ->          `t`.`fragile` AS `t0_c36`, 
    ->          `t`.`status_id` AS `t0_c37`, 
    ->          `t`.`status_address_id` AS `t0_c38`, 
    ->          `t`.`add_status_id` AS `t0_c39`, 
    ->          `t`.`document_id` AS `t0_c40`, 
    ->          `t`.`control` AS `t0_c41`, 
    ->          `t`.`close` AS `t0_c42`, 
    ->          `t`.`cdate` AS `t0_c43`, 
    ->          `t`.`mdate` AS `t0_c44`, 
    ->          `t`.`creator_id` AS `t0_c45`, 
    ->          `t`.`score_weight` AS `t0_c46`, 
    ->          `t`.`pod_comment_id` AS `t0_c47`, 
    ->          `t`.`temp` AS `t0_c48`, 
    ->          `manifests`.`id` AS `t1_c0`, 
    ->          `job`.`id` AS `t2_c0`, 
    ->          `job`.`name` AS `t2_c1`, 
    ->          `job`.`city_from_id` AS `t2_c2`, 
    ->          `job`.`city_to_id` AS `t2_c3`, 
    ->          `job`.`customer_id` AS `t2_c4`, 
    ->          `job`.`service_id` AS `t2_c5`, 
    ->          `job`.`tariff_name_id` AS `t2_c6`, 
    ->          `job`.`delivery_type_id` AS `t2_c7`, 
    ->          `job`.`add_service_ids` AS `t2_c8`, 
    ->          `job`.`filial_id` AS `t2_c9`, 
    ->          `job`.`desc` AS `t2_c10`, 
    ->          `job`.`cdate` AS `t2_c11`, 
    ->          `job`.`mdate` AS `t2_c12`, 
    ->          `job`.`creator_id` AS `t2_c13` 
    ->        FROM `bill` `t`  
    ->          LEFT OUTER JOIN `rel_bill_manifest` `manifests_manifests` ON (`t`.`id`=`manifests_manifests`.`bill_id`) 
    ->          LEFT OUTER JOIN `manifest` `manifests` ON (`manifests`.`id`=`manifests_manifests`.`manifest_id`) 
    ->          LEFT OUTER JOIN `job` `job` ON (`t`.`job_id`=`job`.`id`) GROUP BY t.id
    ->      ) sq;
+----------+
| COUNT(*) |
+----------+
|    88472 |
+----------+
1 row in set (1,44 sec)
после:

Код: Выделить всё

mysql> SELECT COUNT(*) 
    -> FROM (
    ->        SELECT 1 
    ->        FROM `bill` `t`  
    ->          LEFT OUTER JOIN `rel_bill_manifest` `manifests_manifests` ON (`t`.`id`=`manifests_manifests`.`bill_id`) 
    ->          LEFT OUTER JOIN `manifest` `manifests` ON (`manifests`.`id`=`manifests_manifests`.`manifest_id`) 
    ->          LEFT OUTER JOIN `job` `job` ON (`t`.`job_id`=`job`.`id`) GROUP BY t.id
    ->      ) sq;
+----------+
| COUNT(*) |
+----------+
|    88472 |
+----------+
1 row in set (0,46 sec)
rak
Сообщения: 2181
Зарегистрирован: 2010.11.02, 23:40
Контактная информация:

Re: ускорение метода count

Сообщение rak »

там могут быть having, и после этого запрос будет не рабочий :)
ну и вообще в 1й версии фреймверка со сложными запросами в AR беда
Последний раз редактировалось rak 2016.12.13, 21:41, всего редактировалось 1 раз.
maxtorchel
Сообщения: 34
Зарегистрирован: 2013.11.19, 16:51

Re: ускорение метода count

Сообщение maxtorchel »

ну можно же чекать на хэвинг и тд.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: ускорение метода count

Сообщение zelenin »

maxtorchel писал(а):Почему в стандартном фрэймворке не парсят подобным образом строку, ведь это существенно ускоряет работу с большими таблицами?
maxtorchel писал(а):ну можно же чекать на хэвинг и тд.
щас еще накидают пяток примеров, на которые нужно чекать, а фреймворк должен быть максимально простым и понятным, без хаков.
maxtorchel
Сообщения: 34
Зарегистрирован: 2013.11.19, 16:51

Re: ускорение метода count

Сообщение maxtorchel »

Переопределил метод, прирост производительности в реальных гридах до 30%. Например, в одном модуле грузилось 5 сек, после обновления 3.5. Я считаю это более чем оправдано.

ps лично у меня нет запросов с хэвингом и тому подобным, однако кидайте примеры что еще исключить, может кому-то пригодится.

Код: Выделить всё

private function parseCountSql($sql)
    {
        if(!strpos($sql, 'HAVING')){
            $selectIdx = strpos($sql, 'SELECT');
            $fromIdx = strpos($sql, 'FROM');
            $sql = substr_replace($sql, ' 1 ', $selectIdx+6, $fromIdx-$selectIdx-6);
        }
        return $sql;
    }
 
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: ускорение метода count

Сообщение zelenin »

в yii2 например ты сам назначаешь totalItemCount, и это правильно. В yii1 кстати тоже. И не надо фреймворк хачить.
maxtorchel
Сообщения: 34
Зарегистрирован: 2013.11.19, 16:51

Re: ускорение метода count

Сообщение maxtorchel »

да, так лучше, спасибо. вынес в отдельный метод

Код: Выделить всё

    protected function getDataProviderTotalItemCount($criteria)
    {
        $countCriteria = new CDbCriteria($criteria->toArray());
        $countCriteria->select = 't.id';
        $countCriteria->order = '';
        $countCriteria->together = true;
        array_walk($countCriteria->with, function (&$params,$relation){
            $params['select'] = $relation.'.id';
        });

        return static::model()->count($countCriteria);
    }
 
Ответить