Запрос выполняется быстро, но хранимая процедура медленная — перепробовал несколько вещей

#mysql #stored-procedures #temp-tables

#mysql #хранимые процедуры #временные таблицы

Вопрос:

У меня есть хранимая процедура, которая запрашивает значения из 2 таблиц (message и message_archived), которые содержат много данных.

 CREATE PROCEDURE `get_all_message_report`(IN fromDate TIMESTAMP, IN toDate TIMESTAMP, IN period VARCHAR(15))

BEGIN

DECLARE lfromDate TIMESTAMP;
DECLARE ltoDate TIMESTAMP;
DECLARE lperiod VARCHAR(15);
DECLARE dateFormat VARCHAR(15);

SET lfromDate = fromDate;
SET ltoDate = toDate;
SET lperiod = period;
SET dateFormat = "";

IF lperiod = "monthly"
THEN
  SET dateFormat = '%b';
ELSEIF lperiod = "daily"
  THEN
    SET dateFormat = '%Y-%c-%d';
ELSEIF lperiod = "weekly"
  THEN
    SET dateFormat = '%x-%v';
END IF;

CREATE TABLE tempMessages(
  id bigint(20),
  generated_time timestamp,
  direction varchar(255),
  status varchar(255),
  read_status varchar(50),
  type varchar(255),
  from_number varchar(255),
  to_number varchar(255),
  INDEX ind (id,generated_time)
);

INSERT INTO tempMessages(id,generated_time,direction,status,read_status,type,from_number,to_number)
SELECT id,generated_time, direction, status, read_status, type, from_number, to_number
FROM (
     SELECT id,generated_time, direction, status, read_status, type, from_number, to_number
     FROM message WHERE generated_time BETWEEN lfromDate AND ltoDate

     union all

     SELECT id,generated_time, direction, status, read_status, type, from_number, to_number
     FROM message_archived WHERE generated_time BETWEEN lfromDate AND ltoDate
     ) t;

SELECT
  sentMessageTable.date,
  IFNULL(sentMessageTable.sentcount, 0)              AS sentMessageCount,
  IFNULL(newPassMessagesTable.newpassmessagescount, 0)       AS newPassMessagesCount,
  IFNULL(newReviewMessagesTable.newreviewmessagescount, 0)       AS newReviewMessagesCount,
  IFNULL(newFailMessagesTable.newfailmessagescount, 0)       AS newFailMessagesCount,
  IFNULL(respondedPassThreadsTable.respondedpassthreadcount, 0)       AS passThreadsRespondedCount,
  IFNULL(respondedReviewThreadsTable.respondedreviewthreadcount, 0)       AS reviewThreadsRespondedCount,
  IFNULL(respondedFailThreadsTable.respondedfailthreadcount, 0)       AS failThreadsRespondedCount
FROM
  (
      (

        /*  sent message count*/
        SELECT
          date,
          sum(sentcount1) AS sentcount
        FROM
          (
            SELECT
              DATE_FORMAT(generated_time, dateFormat) AS date,
              COUNT(*)                                AS sentcount1
            FROM tempMessages
            WHERE direction = 'outgoing' and status in ('SENT','DELIVERED')
            GROUP BY date
          ) t
        GROUP BY date
        ORDER BY date
      ) AS sentMessageTable LEFT JOIN

      /* new pass messages count */
      (
        SELECT
         date1,
         sum(newpassmessagescount1) AS newpassmessagescount
        FROM
          (
            SELECT
              DATE_FORMAT(generated_time, dateFormat) AS date1,
              COUNT(*)                                AS newpassmessagescount1
            FROM tempMessages
            WHERE direction='incoming'
                  AND read_status='UNREAD' AND type='PASS'
            GROUP BY date1
          ) t
        GROUP BY date1
        ORDER BY date1
      )
      AS newPassMessagesTable
      ON sentMessageTable.date=newPassMessagesTable.date1
      LEFT JOIN

      /* new review messages count */
      (
        SELECT
         date2,
         sum(newreviewmessagescount1) AS newreviewmessagescount
        FROM
          (
            SELECT
              DATE_FORMAT(generated_time, dateFormat) AS date2,
              COUNT(*)                                AS newreviewmessagescount1
            FROM tempMessages
            WHERE direction='incoming'
                  AND read_status='UNREAD' AND type='REVIEW'
            GROUP BY date2
          ) t
        GROUP BY date2
        ORDER BY date2
      )
      AS newReviewMessagesTable
      ON sentMessageTable.date=newReviewMessagesTable.date2
      LEFT JOIN

      /* new fail messages count */
      (
        SELECT
         date3,
         sum(newfailmessagescount1) AS newfailmessagescount
        FROM
          (
            SELECT
              DATE_FORMAT(generated_time, dateFormat) AS date3,
              COUNT(*)                                AS newfailmessagescount1
            FROM tempMessages
            WHERE direction='incoming'
                  AND read_status='UNREAD' AND type='FAIL'
            GROUP BY date3
          ) t
        GROUP BY date3
        ORDER BY date3
      )
      AS newFailMessagesTable
      ON sentMessageTable.date=newFailMessagesTable.date3
      LEFT JOIN

      /*    responded pass thread count*/
      (
        SELECT
          date4,
          count(*) AS respondedpassthreadcount
        FROM
          (
            SELECT
              DATE_FORMAT(generated_time, dateFormat) AS date4,
              from_number,
              to_number,
              COUNT(*),
              MAX(msg.id)                             AS maxId
            FROM tempMessages msg
            GROUP BY date4, msg.from_number, msg.to_number
            HAVING maxId IN
                   (
                     SELECT max(msg.id)
                     FROM tempMessages msg
                     WHERE msg.direction = "outgoing"
                     GROUP BY DATE_FORMAT(generated_time, dateFormat), msg.from_number, msg.to_number
                   ) AND maxId NOT IN (SELECT MIN(msgSub.id)
                                       FROM tempMessages msgSub
                                       GROUP BY msgSub.from_number, msgSub.to_number
                                       HAVING MIN(msgSub.id) = MAX(msgSub.id))
                     AND from_number IN ( select msg1.to_number
                                      from tempMessages msg1
                                      where msg1.direction = 'incoming' and msg1.type='PASS')
                     AND to_number IN ( select msg1.from_number
                                      from tempMessages msg1
                                      where msg1.direction = 'incoming' and msg1.type='PASS')
          ) t
        GROUP BY date4
        ORDER BY date4
      ) AS respondedPassThreadsTable
        ON sentMessageTable.date = respondedPassThreadsTable.date4
      LEFT JOIN

      /*    responded review thread count*/
      (
        SELECT
          date5,
          count(*) AS respondedreviewthreadcount
        FROM
          (
            SELECT
              DATE_FORMAT(generated_time, dateFormat) AS date5,
              from_number,
              to_number,
              COUNT(*),
              MAX(msg.id)                             AS maxId
            FROM tempMessages msg
            GROUP BY date5, msg.from_number, msg.to_number
            HAVING maxId IN
                   (
                     SELECT max(msg.id)
                     FROM tempMessages msg
                     WHERE msg.direction = "outgoing"
                     GROUP BY DATE_FORMAT(generated_time, dateFormat), msg.from_number, msg.to_number
                   ) AND maxId NOT IN (SELECT MIN(msgSub.id)
                                       FROM tempMessages msgSub
                                       GROUP BY msgSub.from_number, msgSub.to_number
                                       HAVING MIN(msgSub.id) = MAX(msgSub.id))
                     AND from_number IN ( select msg1.to_number
                                      from tempMessages msg1
                                      where msg1.direction = 'incoming' and msg1.type='REVIEW')
                     AND to_number IN ( select msg1.from_number
                                      from tempMessages msg1
                                      where msg1.direction = 'incoming' and msg1.type='REVIEW')
          ) t
        GROUP BY date5
        ORDER BY date5
      ) AS respondedReviewThreadsTable
        ON sentMessageTable.date = respondedReviewThreadsTable.date5
      LEFT JOIN

      /*responded fail thread count*/
      (
        SELECT
          date6,
          count(*) AS respondedfailthreadcount
        FROM
          (
            SELECT
              DATE_FORMAT(generated_time, dateFormat) AS date6,
              from_number,
              to_number,
              COUNT(*),
              MAX(msg.id)                             AS maxId
            FROM tempMessages msg
            GROUP BY date6, msg.from_number, msg.to_number
            HAVING maxId IN
                   (
                     SELECT max(msg.id)
                     FROM tempMessages msg
                     WHERE msg.direction = "outgoing"
                     GROUP BY DATE_FORMAT(generated_time, dateFormat), msg.from_number, msg.to_number
                   ) AND maxId NOT IN (SELECT MIN(msgSub.id)
                                       FROM tempMessages msgSub
                                       GROUP BY msgSub.from_number, msgSub.to_number
                                       HAVING MIN(msgSub.id) = MAX(msgSub.id))
                     AND from_number IN ( select msg1.to_number
                                      from tempMessages msg1
                                      where msg1.direction = 'incoming' and msg1.type='FAIL')
                     AND to_number IN ( select msg1.from_number
                                      from tempMessages msg1
                                      where msg1.direction = 'incoming' and msg1.type='FAIL')
          ) t
        GROUP BY date6
        ORDER BY date6
      ) AS respondedFailThreadsTable
        ON sentMessageTable.date = respondedFailThreadsTable.date6

DROP TABLE tempMessages;

END
  

Вместо создания обычной таблицы я попытался использовать временные таблицы
Но я не могу присоединиться к временной таблице несколько раз в одном запросе.
Итак, я попытался использовать обычную таблицу, которая удаляется после выполнения sp
но это также не помогает сократить время загрузки.
при выполнении запроса в sp отдельно я получаю результат через 10-20 секунд

Есть ли решение для сокращения времени загрузки?

Комментарии:

1. A GROUP BY DATE_FORMAT( никогда не бывает эффективным. Подумайте о том, чтобы переписать свои запросы с использованием оконных функций MySQL-8.0 / MariaDB-10.2. Если ваши запросы выполняются быстро без хранимых процедур, оставьте их такими. Их легче отлаживать.

2. @danblack Я не думаю, что изменение версии SQL практически осуществимо в моем случае. Потому что весь проект работает на MySQL 5.7. Есть ли какое-либо решение в MySQL 5.7 для этого?

3. Включить EXPLAIN {query} . Это действительно ужасно, и потребуется некоторое время, чтобы понять. Сгенерированные столбцы для формата даты могут немного помочь.

4. Не могли бы вы уточнить, что работает быстро, а что медленно (и поэтому какая часть нуждается в оптимизации?). Ваш заголовок подразумевает, что запрос (который можно сильно оптимизировать) на самом деле быстрый, просто внутри вашей хранимой процедуры это не так? Это будет / может означать, что остальной материал в вашей процедуре (генерация временной таблицы) является узким местом, и это потребует совершенно другого подхода к оптимизации.