2014-02-04 3 views
0

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

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

Есть ли советы или рекомендации, которые я мог бы использовать для оптимизации или исправления этой инструкции SQL, чтобы ускорить ее.

(EDIT) Я добавил код для создания таблиц.

SELECT 
    case when (select count(ag.`PKEY`) - count(ag.`ANSWERTIME`) from acdcallinformation ag 
    where (ag.`COMPLETED`) = 1 and answertime is null and time(ag.INSTIME) and DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) and ag.skillid = acdcallinformation.skillid) is null 
     then 0 
      else 
     (select count(ag.`PKEY`) - count(ag.`ANSWERTIME`) from acdcallinformation ag where (ag.`COMPLETED`) = 1 
     and answertime is null and DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) and ag.skillid = acdcallinformation.skillid) 
    end as LostCalls, 
    case when count(acdcallinformation.idleonqueue) is null then 0 else count(acdcallinformation.idleonqueue) end as CountCallsACD, 
    case when count(acdcallinformation.`ANSWERTIME`) is null then 0 else count(acdcallinformation.`ANSWERTIME`) end AS acdcallinformation_ANSWERED, 
    (select skillinfo.skillname from skillinfo where skillinfo.pkey = acdcallinformation.skillid) AS acdcallinformation_SKILLIDTEXT, 

    (select count(pkey) from acdcallinformation age 
     where DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) and age.skillid = acdcallinformation.skillid and (age.`COMPLETED`) = 0 and answertime is null 
     and SKILLID in (select SKILLID 
           from 
          callcenterinformation 
          where time > (now() - INTERVAL 5 SECOND) and callswaiting > 0)) as Waiting, 

    -- count(acdcallinformation.`PKEY`) as CallsWaiting, 
    acdcallinformation.`DATEOFCALL` AS acdcallinformation_DATEOFCALL, 
    acdcallinformation.`FIRSTRINGONQUEUE` AS acdcallinformation_FIRSTRINGONQUEUE, 
    case when acdcallinformation.`CONNECTTIME` is null then time('00:00:00') else acdcallinformation.`CONNECTTIME` end AS acdcallinformation_CONNECTTIME, 
    acdcallinformation.`CALLSTATEBEFOREIDLE` AS acdcallinformation_CALLSTATEBEFOREIDLE, 
    case when acdcallinformation.`AGENTRINGTIME` is null then time('00:00:00') else acdcallinformation.`AGENTRINGTIME` end AS acdcallinformation_AGENTRINGTIME, 
    acdcallinformation.`IDLEONQUEUE` AS acdcallinformation_IDLEONQUEUE, 
    acdcallinformation.`DDI` AS acdcallinformation_DDI, 
    acdcallinformation.`CLIP` AS acdcallinformation_CLIP, 
    acdcallinformation.`SKILLID` AS acdcallinformation_SKILLID, 
    acdcallinformation.`ACTIONTYPE` AS acdcallinformation_ACTIONTYPE, 
    acdcallinformation.`ACTIONDESTINATION` AS acdcallinformation_ACTIONDESTINATION, 
    acdcallinformation.`COMPLETED` AS acdcallinformation_COMPLETED, 
    acdcallinformation.`HANDLED` AS acdcallinformation_HANDLED, 
    acdcallinformation.`CONFIRMED` AS acdcallinformation_CONFIRMED, 
    (
     SELECT 
     cal.`AGENTSREADY` AS callcenterinformation_AGENTSREADY 
    FROM 
     `callcenterinformation` cal 
    WHERE cal.skillid <> 1 and acdcallinformation.skillid = skillid order by pkey desc limit 1,1) as agentsready 
FROM 
    `acdcallinformation` acdcallinformation 
where DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()- interval 1 day) 
group by (select skillinfo.skillname from skillinfo where skillinfo.pkey = acdcallinformation.skillid); 


CREATE TABLE `callcenterinformation` (
    `INSTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `PKEY` INT(11) NOT NULL, 
    `SKILLID` INT(11) NULL DEFAULT '0', 
    `DATE` DATE NULL DEFAULT NULL, 
    `TIME` TIME NULL DEFAULT NULL, 
    `AGENTSLOGGEDIN` INT(11) NULL DEFAULT '0', 
    `AGENTSREADY` INT(11) NULL DEFAULT '0', 
    `AGENTSRINGING` INT(11) NULL DEFAULT '0', 
    `AGENTSCONNECTED` INT(11) NULL DEFAULT '0', 
    `AGENTSINPAUSE` INT(11) NULL DEFAULT '0', 
    `AGENTSINWRAPUP` INT(11) NULL DEFAULT '0', 
    `CALLSWAITING` INT(11) NULL DEFAULT '0', 
    `COMPLETED` TINYINT(1) NULL DEFAULT '0', 
    `HANDLED` TINYINT(1) NULL DEFAULT '0', 
    `CONFIRMED` TINYINT(1) NULL DEFAULT '0', 
    PRIMARY KEY (`PKEY`), 
    INDEX `DATE` (`DATE`), 
    INDEX `TIME` (`TIME`), 
    INDEX `SKILLID` (`SKILLID`) 
) 
COLLATE='latin1_swedish_ci' 
ENGINE=InnoDB; 


CREATE TABLE `acdcallinformation` (
    `INSTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `PKEY` INT(11) NOT NULL, 
    `DATEOFCALL` DATE NULL DEFAULT NULL, 
    `FIRSTRINGONQUEUE` TIME NULL DEFAULT NULL, 
    `CONNECTTIME` TIME NULL DEFAULT NULL, 
    `CALLSTATEBEFOREIDLE` INT(11) NULL DEFAULT '0', 
    `AGENTRINGTIME` TIME NULL DEFAULT NULL, 
    `ANSWERTIME` TIME NULL DEFAULT NULL, 
    `IDLEONQUEUE` TIME NULL DEFAULT NULL, 
    `DDI` TEXT NULL, 
    `CLIP` TEXT NULL, 
    `SKILLID` INT(11) NULL DEFAULT '0', 
    `ACTIONTYPE` INT(11) NULL DEFAULT '0', 
    `ACTIONDESTINATION` TEXT NULL, 
    `COMPLETED` TINYINT(1) NULL DEFAULT '0', 
    `HANDLED` TINYINT(1) NULL DEFAULT '0', 
    `CONFIRMED` TINYINT(1) NULL DEFAULT '0', 
    PRIMARY KEY (`PKEY`), 
    INDEX `DATEOFCALL` (`DATEOFCALL`), 
    INDEX `IDLEONQUEUE_HANDLED` (`IDLEONQUEUE`, `HANDLED`), 
    INDEX `SKILLID` (`SKILLID`) 
) 
COLLATE='latin1_swedish_ci' 
ENGINE=InnoDB; 


CREATE TABLE `skillinfo` (
    `INSTIME` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `PKEY` INT(11) NOT NULL, 
    `SKILLNAME` TEXT NULL, 
    `CLIP` TEXT NULL, 
    `WRAPUPTIMELENGTH` INT(11) NULL DEFAULT '0', 
    `MAXRINGTIMELENGTH` INT(11) NULL DEFAULT '0', 
    `FORCEDTICKET` TINYINT(1) NULL DEFAULT '0', 
    `STATEAFTERWRAPUP` INT(11) NULL DEFAULT '0', 
    `STATEAFTERUNANSWEREDCALL` INT(11) NULL DEFAULT '0', 
    `ACTIONTYPE` INT(11) NULL DEFAULT '0', 
    `ACTIONDESTINATION` TEXT NULL, 
    `DEFLECTAFTERCOURTESY` TINYINT(1) NULL DEFAULT '0', 
    `MAXOVERALLRINGTIMELENGTH` INT(11) NULL DEFAULT '0', 
    `AUTOCLIP` TINYINT(1) NULL DEFAULT '0', 
    `OUTGOINGSETTINGSACTIVE` TINYINT(1) NULL DEFAULT '0', 
    `NUMPLANIDENTIFIER` INT(11) NULL DEFAULT '0', 
    `TYPEOFNUMBER` INT(11) NULL DEFAULT '0', 
    `CLIR` INT(11) NULL DEFAULT '0', 
    `OUTGOINGROUTEID` INT(11) NULL DEFAULT '0', 
    `USELASTAGENT` TINYINT(1) NULL DEFAULT '0', 
    `CLIPROUTINGACTIVE` TINYINT(1) NULL DEFAULT '0', 
    `USETHRESHOLD` TINYINT(1) NULL DEFAULT '0', 
    `NORMALLOADTHRESHOLD` INT(11) NULL DEFAULT '0', 
    `OVERLOADTHRESHOLD` INT(11) NULL DEFAULT '0', 
    `STATEAFTERFORWARD` INT(11) NULL DEFAULT '0', 
    `CALLDISTTYPE` INT(11) NULL DEFAULT '0', 
    `USERGROUPID` INT(11) NULL DEFAULT '0', 
    `EXTERNALCONTROL` TINYINT(1) NULL DEFAULT '0', 
    `LASTAGENTLIMIT` INT(11) NULL DEFAULT '0', 
    PRIMARY KEY (`PKEY`) 
) 
COLLATE='latin1_swedish_ci' 
ENGINE=InnoDB; 

ответ

1

Слишком честно, есть так много «неправильных» с этим запросом он просто не забавой =/

Некоторые мысли:

  • IFNULL() намного более читаемым, чем CASE WHEN <field> IS NULL THEN constant ELSE <field> END , особенно если окажется подзапросом.
  • AFAIK COUNT(*) всегда будет возвращать 0, даже если ничего не найдено. Таким образом, нет необходимости писать IFNULL() вокруг него.
  • COUNT(field) подсчитывает только записи, отличные от NULL для этого поля, но опять же, если ничего не найдено, оно вернет 0, поэтому нет необходимости в IFNULL() вокруг это
  • Вы должны научить себя, как JOIN таблиц, поскольку это (намного) лучше, чем использование коррелированных подзапросов по всему месту.
  • Я мало знаю о mysql, но мне кажется, что вы убиваете свою производительность, ставя роли и функции вокруг полей, которые в противном случае имеют полезный индекс. Я уверен, что из-за этих конструкций двигатель просто не может использовать указанные индексы, что приводит к снижению производительности. например. Я хотел бы попробовать переписать
    • AND DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) в нечто вроде AND DATEOFCALL >= CUR_DATE(), в конце концов, обе стороны даты (= число)
    • DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()- interval 1 day) в DATEOFCALL >= date(now()- interval 1 day) по той же самой причине
    • Я также не уверен, что должен делать time(ag.INSTIME) ?!?! Это правда, когда время отличается от 00:00:00?
  • Я очень удивлен, этот запрос на самом деле компилирует вообще, как вы, кажется, GROUP BY на просто skillname, но и принести довольно много других полей из таблицы (например, idleonqueue). Из фона MSSQL, который не должен работать. Я думаю, что mysql отличается, хотя я задаюсь вопросом, каким будет результат.

В любом случае, пытаясь применить некоторые из приведенных выше вопросов к вашему запросу, я в итоге приведу ниже. Я сомневаюсь, что это будет «намного быстрее»; это может быть немного, но я бы счел это шагом вперед в вашей миссии по очистке его далее ...

Удачи вам!

SELECT (SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) 
      FROM acdcallinformation ag 
     WHERE ag.`COMPLETED` = 1 
      AND answertime is null      
      AND time(ag.INSTIME) 
      AND ag.DATEOFCALL >= CURDATE()    
      AND ag.skillid = info.skillid) AS LostCalls, 
     COUNT(info.idleonqueue) AS CountCallsACD, 
     COUNT(info.`ANSWERTIME`) AS acdcallinformation_ANSWERED, 
     skillinfo.skillname AS acdcallinformation_SKILLIDTEXT, 
     (SELECT COUNT(pkey) 
     FROM acdcallinformation age 
     WHERE age.DATEOFCALL >= CURDATE() 
      AND age.skillid = info.skillid 
      AND age.`COMPLETED` = 0 
      AND age.answertime is null 
      AND age.SKILLID IN (SELECT SKILLID 
           FROM callcenterinformation cci 
           WHERE cci.time > (now() - INTERVAL 5 SECOND) 
           AND cci.callswaiting > 0)) AS Waiting, 
     -- count(info.`PKEY`) AS CallsWaiting, 
     info.`DATEOFCALL` AS acdcallinformation_DATEOFCALL, 
     info.`FIRSTRINGONQUEUE` AS acdcallinformation_FIRSTRINGONQUEUE, 
     IFNULL(info.`CONNECTTIME`, time('00:00:00')) AS acdcallinformation_CONNECTTIME, 
     info.`CALLSTATEBEFOREIDLE` AS acdcallinformation_CALLSTATEBEFOREIDLE, 
     IFNULL(info.`AGENTRINGTIME`, time('00:00:00')) AS acdcallinformation_AGENTRINGTIME, 
     info.`IDLEONQUEUE` AS acdcallinformation_IDLEONQUEUE, 
     info.`DDI` AS acdcallinformation_DDI, 
     info.`CLIP` AS acdcallinformation_CLIP, 
     info.`SKILLID` AS acdcallinformation_SKILLID, 
     info.`ACTIONTYPE` AS acdcallinformation_ACTIONTYPE, 
     info.`ACTIONDESTINATION` AS acdcallinformation_ACTIONDESTINATION, 
     info.`COMPLETED` AS acdcallinformation_COMPLETED, 
     info.`HANDLED` AS acdcallinformation_HANDLED, 
     info.`CONFIRMED` AS acdcallinformation_CONFIRMED, 
     (SELECT cal.`AGENTSREADY` AS callcenterinformation_AGENTSREADY 
      FROM `callcenterinformation` cal 
     WHERE cal.skillid <> 1 
      AND cal.skillid = info.skillid 
     ORDER BY pkey DESC LIMIT 1,1) AS agentsready 
    FROM `acdcallinformation` info 
    JOIN `skillinfo` 
    ON skillinfo.pkey = info.skillid 
WHERE info.DATEOFCALL >= (date(now()- interval 1 day)) 
GROUP BY skillinfo.skillname ; 
+0

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

Смежные вопросы