На выполнение моего запроса уходит слишком много времени?

Я получаю данные о потенциальных клиентах пользователей из базы данных, содержащей миллион записей, и получение данных занимает 5-10 секунд. Как мне сделать это быстрее? Следует ли мне переключиться с ORM на прямой запрос или я могу что-то сделать с текущим ORM?

public static function getBtqUsers($search_params) {
    $criteria = new Criteria();

    $criteria->clearSelectColumns();
    $criteria->addSelectColumn("btq_user.id as id");
    $criteria->addSelectColumn("btq_user.drid as drid");
    $criteria->addSelectColumn("btq_user.name as name");
    $criteria->addSelectColumn("btq_user.email as email");
    $criteria->addSelectColumn("btq_user.active as active");
    $criteria->addSelectColumn("btq_user.lead_from as lead_from");
    $criteria->addSelectColumn("btq_user.source as source");
    $criteria->addSelectColumn("btq_user.http_referer as http_referer");
    $criteria->addSelectColumn("btq_user.sms_status as sms_status");
    $criteria->addSelectColumn("btq_user.telefon as telefon");
    $criteria->addSelectColumn("btq_user.datain as datain");
    $criteria->addSelectColumn("btq_user.kall as kall");
    $criteria->addSelectColumn("btq_user.ip_address as ip_address");

    $criteria->addSelectColumn("count(btq_user_track_blog_video.counter) as total_video_viewed");

    $criteria->addSelectColumn("btq_doctors.id as dr_id");
    $criteria->addSelectColumn("btq_doctors.drname as dr_name");
    $criteria->addSelectColumn("btq_doctors.drphone as dr_phone");
    $criteria->addSelectColumn("btq_doctors.initials as dr_initials");
    $criteria->addSelectColumn("btq_doctors.drmail as dr_mail");

    $criteria->addSelectColumn("event_data.btq_user_id as event_user_id");
    $criteria->addSelectColumn("count(if (event_data.event_parent_id=2, event_data.event_parent_id, null)) as kms_total_video_viewed");
    $criteria->addSelectColumn("count(if (event_data.event_parent_id=1, event_data.event_parent_id, null)) as kms_total_blog_viewed");

    $criteria->addSelectColumn("state.pres as pres");
    $criteria->addSelectColumn("state.state as state");

    $criteria->addSelectColumn("btq_user_sales_choice.type as choice_type");
    $criteria->addSelectColumn("btq_user_sales_choice.opt_value as choice_value");
    $criteria->addSelectColumn("btq_user_sales_choice.opt_text as choice_text");
    $criteria->addSelectColumn("btq_user.pfu_customer_id as pfu_customer_id");
    $criteria->addSelectColumn("lead_schedule.id as schedule_id");
    $criteria->addSelectColumn("lead_schedule.created_at as schedule_date");

    $criteria->addJoin(self::STATE_ID, StatePeer::ID, Criteria::LEFT_JOIN);
    $criteria->addJoin(self::BTQ_USER_SALES_CHOICE_ID, BtqUserSalesChoicePeer::ID, Criteria::LEFT_JOIN);
    $criteria->addJoin(self::ID, LeadSchedulePeer::LEAD_ID, Criteria::LEFT_JOIN);
    $criteria->addJoin(self::DRID, BtqDoctorsPeer::ID, Criteria::LEFT_JOIN);
    $criteria->addJoin(self::ID, BtqUserTrackBlogVideoPeer::USER_ID, Criteria::LEFT_JOIN);
//    $criteria->addJoin(self::DRID, BtqDoctorPfuAdminPeer::BTQ_DOCTOR_ID, Criteria::LEFT_JOIN);
//    $criteria->addExtraJoin(BtqDoctorPfuAdminPeer::PFU_ADMIN_ID, PfuCustomerPeer::PFU_ADMIN_ID, Criteria::LEFT_JOIN, " AND " . PfuCustomerPeer::EMAIL . " = " . self::EMAIL);

    $criteria->addJoin(self::ID, EventDataPeer::BTQ_USER_ID, Criteria::LEFT_JOIN);

    //$criteria->addGroupByColumn(self::ID);
    $criteria->addGroupByColumn(self::EMAIL);
    //$criteria->addGroupByColumn(EventDataPeer::BTQ_USER_ID);
    $criteria->add(BtqUserPeer::IS_DUMMY_DETAIL, "1", Criteria::NOT_EQUAL);

    $criteria->addDescendingOrderByColumn(self::DATAIN);
    if (!empty($search_params)) {
      foreach ($search_params as $key => $param) {

        if (trim($param)) {
          $param = addslashes($param);

          switch ($key) {

            case 'name':
              $criteria->add(BtqUserPeer::NAME, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'email':
              $criteria->add(BtqUserPeer::EMAIL, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'dr_name':
              $criteria->add(BtqDoctorsPeer::DRNAME, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'phone':
              $criteria->add(BtqUserPeer::TELEFON, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'lead_from':
              $criteria->add(BtqUserPeer::LEAD_FROM, $param, Criteria::EQUAL);
              break;

            case 'start_date':
              $criteria->add(BtqUserPeer::DATAIN, $param . " 00:00:00", Criteria::GREATER_EQUAL);
              break;

            case 'end_date':
              $criteria->addAnd(BtqUserPeer::DATAIN, $param . " 23:59:59", Criteria::LESS_EQUAL);
              break;

            case 'location':
              if ($param == "Local")
                $criteria->add(StatePeer::PRES, array("MD", "VA", "DC"), Criteria::IN);
              else if ($param == "Non Local")
                $criteria->add(StatePeer::PRES, array("MD", "VA", "DC"), Criteria::NOT_IN);
              break;
          }
        }
      }
    }//FB::log($criteria);
    return $criteria;
//    return self::doSelectStmt($criteria)->fetchAll();
  }

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

Код, который я передаю в качестве параметра searchParams:

> $searchParams = array('start_date' => date('Y-m-d', strtotime('-2
> days')) , 'end_date' => date("Y-m-d"));
>       $this->pager->setCriteria(btqAdminBtqUserPeer::getBtqUsers($searchParams));
>       $this->form->setDefault('start_date', $searchParams['start_date']);
>       $this->form->setDefault('end_date', $searchParams['end_date']);

RAW SQL:

SELECT  btq_user.id as id, btq_user.drid as drid, btq_user.name as name,
        btq_user.email as email, btq_user.active as active, btq_user.lead_from as lead_from,
        btq_user.source as source, btq_user.http_referer as http_referer,
        btq_user.sms_status as sms_status, btq_user.telefon as telefon,
        btq_user.datain as datain, btq_user.kall as kall, btq_user.ip_address as ip_address,
        count(btq_user_track_blog_video.counter) as total_video_viewed,
        btq_doctors.id as dr_id, btq_doctors.drname as dr_name,
        btq_doctors.drphone as dr_phone, btq_doctors.initials as dr_initials,
        btq_doctors.drmail as dr_mail, event_data.btq_user_id as event_user_id,
        count(if (event_data.event_parent_id=2, event_data.event_parent_id,
                        null)) as kms_total_video_viewed,
        count(if (event_data.event_parent_id=1,
                        event_data.event_parent_id, null)
             ) as kms_total_blog_viewed,
        state.pres as pres, state.state as state, btq_user_sales_choice.type as choice_type,
        btq_user_sales_choice.opt_value as choice_value, btq_user_sales_choice.opt_text as choice_text,
        btq_user.pfu_customer_id as pfu_customer_id, lead_schedule.id as schedule_id,
        lead_schedule.created_at as schedule_date
    FROM  btq_user
    LEFT JOIN  state  ON (btq_user.STATE_ID=state.ID)
    LEFT JOIN  btq_user_sales_choice
         ON (btq_user.BTQ_USER_SALES_CHOICE_ID=btq_user_sales_choice.ID)
    LEFT JOIN  lead_schedule  ON (btq_user.ID=lead_schedule.LEAD_ID)
    LEFT JOIN  btq_doctors  ON (btq_user.DRID=btq_doctors.ID)
    LEFT JOIN  btq_user_track_blog_video
         ON (btq_user.ID=btq_user_track_blog_video.USER_ID)
    LEFT JOIN  event_data  ON (btq_user.ID=event_data.BTQ_USER_ID)
    WHERE  btq_user.IS_DUMMY_DETAIL<>1
      AND  (btq_user.DATAIN>='2008-03-11 00:00:00'
      AND   btq_user.DATAIN<='2018-03-13 23:59:59'
           )
    GROUP BY  btq_user.EMAIL
    ORDER BY  btq_user.DATAIN DESC
    LIMIT  20

Вид:

<?php
      foreach ($pager->getResults() as $row):
        $customer = btqAdminBtqUserPeer::getQuizUserGroupByEmaiIdAndTelephone($row['email'], $params);
        if ($customer['quizes']) {
          $quizzes = explode(",", $customer['quizes']);
          $quizzes_count = count($quizzes);
        } else {
          $quizzes = '';
          $quizzes_count = 0;
        }

        $scheduleClass = ($row['schedule_date']) ? 'schedule_available':'schedule_empty';
        ?>

Из шаблона / представления я вызываю приведенную ниже функцию, которая сильно замедляет запрос:

public static function getQuizUserGroupByEmaiIdAndTelephone($userEmailId, $searchParameters) {
    $criteria = new Criteria();
    $criteria->addSelectColumn(" IF( COUNT(btq_user.id) > 1, GROUP_CONCAT(CONCAT(btq_user.id),'~',CONCAT(btq_user.datain)) , null) as quizes");
    $criteria->add(BtqUserPeer::EMAIL, $userEmailId, Criteria::EQUAL);
    $criteria->add(BtqUserPeer::IS_DUMMY_DETAIL, "1", Criteria::NOT_EQUAL);
    if (!empty($searchParameters)) {
      foreach ($searchParameters as $key => $param) {

        if (trim($param)) {
          $param = addslashes($param);

          switch ($key) {

            case 'name':
              $criteria->add(BtqUserPeer::NAME, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'email':
              $criteria->add(BtqUserPeer::EMAIL, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'dr_name':
              $criteria->add(BtqDoctorsPeer::DRNAME, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'phone':
              $criteria->add(BtqUserPeer::TELEFON, "%" . $param . "%", Criteria::LIKE);
              break;

            case 'lead_from':
              $criteria->add(BtqUserPeer::LEAD_FROM, $param, Criteria::EQUAL);
              break;

            case 'start_date':
              $criteria->add(BtqUserPeer::DATAIN, $param . " 00:00:00", Criteria::GREATER_EQUAL);
              break;

            case 'end_date':
              $criteria->addAnd(BtqUserPeer::DATAIN, $param . " 23:59:59", Criteria::LESS_EQUAL);
              break;

            case 'location':
              $criteria->addJoin(self::STATE_ID, StatePeer::ID, Criteria::LEFT_JOIN);
              if ($param == "Local")
                $criteria->add(StatePeer::PRES, array("MD", "VA", "DC"), Criteria::IN);
              else if ($param == "Non Local")
                $criteria->add(StatePeer::PRES, array("MD", "VA", "DC"), Criteria::NOT_IN);
              break;
          }
        }
      }
    }
$criteria->addDescendingOrderByColumn(self::DATAIN);
return self::doSelectStmt($criteria)->fetch(PDO::FETCH_ASSOC);

Если потребуется что-то еще, дайте мне знать, я отредактирую вопрос и описание.

Спасибо

Создайте представление для всех объединений таблиц и выполните фильтрацию в этом представлении.

Sumesh TG 12.03.2018 16:04

Проверьте, требует ли времени запрос к базе данных (например, условие where без индекса). Для этого вам нужно профилировать соединяемые части SQL вашего запроса. Если ваш запрос возвращает тысячи строк, возможно, это требует времени для гидратации объекта, и вы могли бы рассмотреть возможность использования собственного запроса или разбивки вашего приложения на страницы.

Mcsky 12.03.2018 16:51

Пожалуйста, предоставьте SQL, сгенерированный этой утомительно многословной ORM. Может быть, мы сможем предложить лучший SQL или INDEX, после чего вы сможете вернуться к ORM-языку.

Rick James 13.03.2018 03:16

@RickJames добавил, пожалуйста, проверьте обновление

DojoDev 13.03.2018 10:24
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
Освоение архитектуры микросервисов с Laravel: Лучшие практики, преимущества и советы для разработчиков
В последние годы архитектура микросервисов приобрела популярность как способ построения масштабируемых и гибких приложений. Laravel , популярный PHP...
Как построить CRUD-приложение в Laravel
Как построить CRUD-приложение в Laravel
Laravel - это популярный PHP-фреймворк, который позволяет быстро и легко создавать веб-приложения. Одной из наиболее распространенных задач в...
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
Освоение PHP и управление базами данных: Создание собственной СУБД - часть II
В предыдущем посте мы создали функциональность вставки и чтения для нашей динамической СУБД. В этом посте мы собираемся реализовать функции обновления...
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
Роли и разрешения пользователей без пакета Laravel 9
Роли и разрешения пользователей без пакета Laravel 9
Этот пост изначально был опубликован на techsolutionstuff.com .
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
Как установить LAMP Stack - Security 5/5 на виртуальную машину Azure Linux VM
В предыдущей статье мы завершили установку базы данных, для тех, кто не знает.
1
4
104
1

Ответы 1

Нет необходимости во всех столбцах, если вы только получаете COUNT(*). LEFT JOINs не влияет на счет (в этом случае). Я думаю, это даст вам тот же счет:

        SELECT COUNT(DISTINCT btq_user.EMAIL)
            FROM  `btq_user`
            WHERE  btq_user.IS_DUMMY_DETAIL<>1
              AND  btq_user.DATAIN<='2018-02-28 23:59:59'

И этот индекс май поможет:

INDEX(DATAIN)

Более

Это не имеет смысла:

SELECT  IF( COUNT(btq_user.id) > 1,
                   GROUP_CONCAT(CONCAT(btq_user.id),
                                '~',CONCAT(btq_user.datain)) ,
                   null ) as quizes
    FROM  `btq_user`
    WHERE  btq_user.EMAIL='[email protected]'
      AND  btq_user.IS_DUMMY_DETAIL<>1
      AND  (btq_user.DATAIN>='2008-03-11 00:00:00'
      AND   btq_user.DATAIN<='2018-03-13 23:59:59'
           )
    ORDER BY  btq_user.DATAIN DESC

Строка всего одна (из-за агрегатной функции COUNT), так что ORDER BY работать не с чем. Возможно, вам нужен ORDER BY _inside_ theGROUP_CONCAT`?

Для этого нужен INDEX(email, datain)

Спасибо, но я обновил запрос, пожалуйста, проверьте. Старый добавили ошибочно. Спасибо.

DojoDev 13.03.2018 19:13

@DojoDev - проверьте значения из COUNTs. JOIN + GROUP BY мог завышать значения. Убедитесь, что они верны, прежде чем я попытаюсь ускорить запрос.

Rick James 13.03.2018 19:23

пожалуйста, проверьте это вот подробности страницы, которую я получил с панели инструментов отладки. pastebin.com/H8TzCuR1

DojoDev 13.03.2018 19:36

Я прокомментировал один из запросов.

Rick James 13.03.2018 19:47

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

DojoDev 14.03.2018 07:13

Вы имеете в виду MySQL VIEW? Или какое-то другое использование термина «взгляд»?

Rick James 14.03.2018 11:43

Под представлением я подразумеваю шаблон в Symfony, а не в представлении mysql

DojoDev 14.03.2018 16:37

Другие вопросы по теме