Оптимизируйте этот запрос для 1000000+ строк - PullRequest
0 голосов
/ 13 марта 2019

Мне нужно вытащить данные и записать их в CSV-файл, но это занимает слишком много времени и слишком много оперативной памяти. Что с ним не так и что я могу сделать? Кроме того, я чувствую, что в самом запросе есть избыточность. Я делаю это с PHP.

Вот запрос

CREATE TEMPORARY TABLE temp1 SELECT * FROM vicidial_closer_log
USE INDEX(call_date)
WHERE call_date BETWEEN '1980-01-01 00:00:00' AND '2019-03-12 23:59:59';
CREATE TEMPORARY TABLE temp2 SELECT * FROM vicidial_closer_log
USE INDEX(call_date)
WHERE call_date BETWEEN '1980-01-01 00:00:00' AND '2019-03-12 23:59:59';
SELECT a.call_date,
   a.lead_id,
   a.phone_number
   AS customer_number,
   IF(a.status != 'DROP', 'ANSWERED', 'UNANSWERED')
   AS status,
   IF(a.lead_id IS NOT NULL, 'inbound', 'outbound')
   AS call_type,
   a.USER
   AS agent,
   a.campaign_id
   AS skill,
   NULL
   AS campaign,
   a.status
   AS disposition,
   a.term_reason
   AS Hangup,
   a.uniqueid,
   Sec_to_time(a.queue_seconds)
   AS time_to_answer,
   Sec_to_time(a.length_in_sec - a.queue_seconds)
   AS talk_time,
   Sec_to_time(a.park_sec)
   AS hold_sec,
   Sec_to_time(a.dispo_sec)
   AS wrapup_sec,
   From_unixtime(a.start_epoch)
   AS start_time,
   From_unixtime(a.end_epoch)
   AS end_time,
   c.USER
   AS
   transfered,
   a.comments,
IF(a.length_in_sec IS NULL, Sec_to_time(a.queue_seconds),
Sec_to_time(a.length_in_sec + a.dispo_sec))
   AS duration,
Sec_to_time(a.length_in_sec - a.queue_seconds + a.dispo_sec)
   AS handling_time
FROM   temp1 a
   left outer join temp2 c
                   ON a.uniqueid = c.uniqueid
                   AND a.closecallid < c.closecallid
GROUP  BY a.closecallid

Я загрузил скриншот структуры таблицы и индексов. Структура таблицы Индексы таблицы

Спасибо.

UPDATE: ПОКАЗАТЬ CREATE TABLE vicidial_closer_log

vicidial_closer_log     CREATE TABLE `vicidial_closer_log` (
`closecallid` int(9) unsigned NOT NULL AUTO_INCREMENT,
`lead_id` int(9) unsigned NOT NULL,
`list_id` bigint(14) unsigned DEFAULT NULL,
`campaign_id` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
`call_date` datetime DEFAULT NULL,
`start_epoch` int(10) unsigned DEFAULT NULL,
`end_epoch` int(10) unsigned DEFAULT NULL,
`length_in_sec` int(10) DEFAULT NULL,
`status` varchar(6) COLLATE utf8_unicode_ci DEFAULT NULL,
`phone_code` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
`phone_number` varchar(18) COLLATE utf8_unicode_ci DEFAULT NULL,
`user` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
`comments` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`processed` enum('Y','N') COLLATE utf8_unicode_ci DEFAULT NULL,
`queue_seconds` decimal(7,2) DEFAULT 0.00,
`user_group` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
`xfercallid` int(9) unsigned DEFAULT NULL,
`term_reason`       enum('CALLER','AGENT','QUEUETIMEOUT','ABANDON','AFTERHOURS','HOLDRECALLXFER',    'HOLDTIME','NOAGENT','NONE','MAXCALLS') COLLATE utf8_unicode_ci DEFAULT   'NONE',
 `uniqueid` varchar(20) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
 `agent_only` varchar(20) COLLATE utf8_unicode_ci DEFAULT '',
 `queue_position` smallint(4) unsigned DEFAULT 1,
 `called_count` smallint(5) unsigned DEFAULT 0,
 `nopaperform` varchar(5) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'NO',
 `park_sec` int(3) DEFAULT 0,
 `dispo_sec` int(3) DEFAULT 0,
 `record_file` text COLLATE utf8_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`closecallid`),
 KEY `lead_id` (`lead_id`),
 KEY `call_date` (`call_date`),
 KEY `campaign_id` (`campaign_id`),
 KEY `uniqueid` (`uniqueid`),
 KEY `phone_number` (`phone_number`),
 KEY `date_user` (`call_date`,`user`),
 KEY `closecallid` (`closecallid`)
) ENGINE=MyISAM AUTO_INCREMENT=1850672 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci

ОБЪЯСНИТЬ ЗАПРОС (только по третьему запросу):

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   SIMPLE  a   ALL     NULL    NULL    NULL    NULL    664640  Using temporary; Using filesort
1   SIMPLE  c   ALL     NULL    NULL    NULL    NULL    662480  Using where; Using join buffer (flat, BNL join)

ОБНОВЛЕНИЕ (обновленный запрос):

SELECT a.call_date,
       a.lead_id,
       a.phone_number
       AS customer_number,
       IF(a.status != 'DROP', 'ANSWERED', 'UNANSWERED')
       AS status,
       IF(a.lead_id IS NOT NULL, 'inbound', 'outbound')
       AS call_type,
       a.user
       AS agent,
       a.campaign_id
       AS skill,
       NULL
       AS campaign,
       a.status
       AS disposition,
       a.term_reason
       AS Hangup,
       a.uniqueid,
       Sec_to_time(a.queue_seconds)
       AS time_to_answer,
       Sec_to_time(a.length_in_sec - a.queue_seconds)
       AS talk_time,
       Sec_to_time(a.park_sec)
       AS hold_sec,
       Sec_to_time(a.dispo_sec)
       AS wrapup_sec,
       From_unixtime(a.start_epoch)
       AS start_time,
       From_unixtime(a.end_epoch)
       AS end_time,
       c.user
       AS
transfered,
a.comments,
IF(a.length_in_sec IS NULL, Sec_to_time(a.queue_seconds),
Sec_to_time(a.length_in_sec + a.dispo_sec))
       AS duration,
Sec_to_time(a.length_in_sec - a.queue_seconds + a.dispo_sec)
       AS handling_time
FROM   vicidial_closer_log a
       LEFT OUTER JOIN vicidial_closer_log c
                    ON a.closecallid <> c.closecallid
                       AND a.uniqueid = c.uniqueid
                       AND a.closecallid < c.closecallid
WHERE a.call_date BETWEEN '2018-01-01 00:00:00' AND '2019-03-13 23:59:59'

ОБЪЯСНИТЬ об обновленном запросе:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   SIMPLE  a   ALL     call_date,date_user     NULL    NULL    NULL    662829  Using where
1   SIMPLE  c   ref     PRIMARY,uniqueid,closecallid    uniqueid    62  aastell_bliss.a.uniqueid    1   Using where

Обновлен результат выполнения запроса:

Number of rows present between given time range: 155016 rows
Time taken: 0.0149 secs

Работает!

1 Ответ

0 голосов
/ 13 марта 2019

Сводка комментариев, которые приводят к ответу:

  • CREATE TEMPORARY TABLE ... SELECT не создает индексы для временной таблицы
  • Явное использование временной таблицы, особенно большого размера, редко дает выигрыш в производительности.
  • Использование псевдонимов таблиц в объединении позволяет самостоятельное объединение
  • Группировка по первичному ключу в левой части объединения не добавляет ничего, поскольку оно уже уникально, а JOIN не имеет агрегатных выражений. GROUP BY добавляет неявный ORDER BY, чтобы выражение могло закончиться медленнее, если для присоединения к таблице использовался вторичный индекс.
  • Несмотря на то, что диапазон дат запроса был большим, подготовка к тому, чтобы он стал значительным фильтром, когда маленький, сделает call_date более выгодным в качестве индекса. Чтобы сделать это более благоприятным, ключ соединения добавляется в конец индекса, поэтому большая часть работы объединения может выполняться только путем просмотра индекса.
  • Когда PK находится в столбце, вторичный индекс в этом же столбце не требуется.
...