简体   繁体   中英

MySQL - Optimise Query

I have been left with a report which returns rows at an awfully slow rate. I feel I need to redo without so many or any sub-queries. But I am having a complete brain freeze to how to attempt this.

I have looked at indexes and the keys are not unique enough, which forces a full table scan time. Is there any way I can pull certain information from the other tables using a separate query, adding that as a variable and use it in the main query. As really the result of this query is only a few lines.

Are there any tips or tricks, that I could use to optimise or correct this SQL statement to speed it up.

(EDIT) I have added some create code for the tables.

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;

Too be honest, there is so much 'wrong' with this query it just isn't fun anymore =/

Some thoughts:

  • IFNULL() is much more readable than CASE WHEN <field> IS NULL THEN constant ELSE <field> END , especially if turns out to be a sub-query.
  • AFAIK COUNT(*) will always return 0, even if nothing is found. Thus, there is no need to write an IFNULL() around it
  • COUNT(field) only counts the non-NULL records for this field, but again, if nothing is found it will return 0, so no need for an IFNULL() around it
  • You should teach yourself how to JOIN tables as it's (much) better practice than using correlated sub-queries all over the place.
  • I don't know much about mysql, but it seems to me that you're killing your performance by putting casts and functions around the fields that otherwise seem to have a useful index. I'm pretty sure that due to these constructions the engine simply is not able to use said indexes causing performance to go down the drain. eg. I would try to rewrite
    • AND DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()) into something like AND DATEOFCALL >= CUR_DATE() , after all, both sides are dates (= numbers)
    • DATE_FORMAT(DATEOFCALL,'%Y-%m-%d') >= date(now()- interval 1 day) into DATEOFCALL >= date(now()- interval 1 day) for the very same reason
    • I'm also not sure what time(ag.INSTIME) should do ?!?! Is it true whenever the time is different from 00:00:00 ?
  • I'm VERY surprised this query actually compiles at all as you seem to GROUP BY on just the skillname , but also fetch quite a lot of other fields from the table (eg idleonqueue ). From an MSSQL background that should not work.. I guess mysql is different although I do wonder what the result will be like.

Anyway, trying to apply some of the above to your query I end up with below. I doubt it will be 'much faster'; it might be just a bit, but I'd consider it a step forward in your mission to clean it up further...

Good luck!

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 ;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM