#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. Не могли бы вы уточнить, что работает быстро, а что медленно (и поэтому какая часть нуждается в оптимизации?). Ваш заголовок подразумевает, что запрос (который можно сильно оптимизировать) на самом деле быстрый, просто внутри вашей хранимой процедуры это не так? Это будет / может означать, что остальной материал в вашей процедуре (генерация временной таблицы) является узким местом, и это потребует совершенно другого подхода к оптимизации.