2013-02-07 40 views
3

對於「技能名稱」的子查詢選擇而言,以下查詢相當慢。當我針對SQL執行運行配置文件時,我從ACDCallinformation表中針對skillname的子查詢獲得每行太多的查詢。SQL - 調用子查詢太多

優化此SQL查詢的最佳方式是什麼?或者是否有MySQL工具來幫助檢查SQL查詢的成本並優化腳本?

SELECT 
CASE 
    WHEN(
      SELECT 
       COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) 
      FROM acdcallinformation ag 
      WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall = 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 DATEofcall= DATE(NOW()) AND ag.skillid = acdcallinformation.skillid) 
     END AS 'Lost Calls', 
     CASE WHEN COUNT(acdcallinformation.idleonqueue) IS NULL THEN 0 ELSE COUNT(acdcallinformation.idleonqueue) END AS 'Total Calls', 
     CASE WHEN COUNT(acdcallinformation.`ANSWERTIME`) IS NULL THEN 0 ELSE COUNT(acdcallinformation.`ANSWERTIME`) END AS 'Answered', 
    (
     SELECT 
      skillinfo.skillname 
     FROM skillinfo 
     WHERE skillinfo.pkey = acdcallinformation.skillid 
    ) AS Skill, 
    SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time', 
    SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time' 
FROM `acdcallinformation` acdcallinformation 
WHERE DATEOFCALL = DATE(NOW()) 
GROUP BY skill;  

不知道最好的方式顯示數據:

ACDCALLINFORMATION - 行目前3028

INSTIME    PKEY DATEOFCALL CONNECTTIME FIRSTRING SKILLID 
2012-07-19 14:50:16 19985 2012-07-19 14:50:16 14:50:16 5 

SKILLINFO數 - 行的平均數量爲5-10

INSTIME    PKEY SKILLNAME 
2012-07-01 13:12:01 1  Calls Outgoing 
2012-07-01 13:12:01 2  Call Centre 
2012-07-01 13:12:01 3  Accounts 
2012-07-01 13:12:01 4  Reception 

這是預期的輸出:

"Lost Calls" "Total Calls" "Answered" "Skill"   "Average Answer Time" "Average Talk Time" 

"1"   "2"   "1"   "Accounts" "00:00:04" "00:00:01" 
"0"   "5"   "5"   "Service" "00:00:07" "00:01:20" 
+3

你想做什麼?你可以發表表格結構,樣本數據和所需的輸出。我們可能能夠提供一些優化的查詢 –

回答

3

試試這個,是使用內部連接,以提高性能和避免不必要的subquerys

SELECT 
    COALESCE(ag.skillcount, 0) AS 'Lost Calls', 
    CASE WHEN COUNT(acdcallinformation.idleonqueue) IS NULL THEN 0 ELSE COUNT(acdcallinformation.idleonqueue) END AS 'Total Calls', 
    CASE WHEN COUNT(acdcallinformation.`ANSWERTIME`) IS NULL THEN 0 ELSE COUNT(acdcallinformation.`ANSWERTIME`) END AS 'Answered', 
    si.skillname AS Skill, 
    SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time', 
    SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time' 
FROM `acdcallinformation` acdcallinformation 
LEFT JOIN (
    SELECT skillid, COUNT(`PKEY`) - COUNT(`ANSWERTIME`) skillcount 
    FROM acdcallinformation 
    WHERE (`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall = DATE(NOW()) 
) ag ON AND ag.skillid = acdcallinformation.skillid 
LEFT JOIN skillinfo si ON si.pkey = acdcallinformation.skillid 
WHERE DATEOFCALL = DATE(NOW()) 
GROUP BY si.skillname; 
+1

+1來提供'JOIN'替代方案。請注意,內聯子查詢相當於(''LEFT')外連接,而不是內連接。這個查詢可能會丟失一些數據。 –

+1

而且'CASE當ag.skillcount是NULL那麼0 ELSE ag.skillcount END'可以寫成'COALESCE(ag.skillcount,0)' –

+0

是的,謝謝,我用LEFT和COALESCE改變代碼 – Rednaxel

2

它看起來像你試圖確保NULL s被轉換爲0 s。因此:

SELECT 
    IFNULL(
    (SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) FROM acdcallinformation ag 
     WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL 
     AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid 
    ), 0) AS 'Lost Calls', 
    IFNULL(COUNT(acdcallinformation.idleonqueue), 0) AS 'Total Calls', 
    IFNULL(COUNT(acdcallinformation.`ANSWERTIME`),0) AS 'Answered', 
    (
     SELECT 
      skillinfo.skillname 
     FROM skillinfo 
     WHERE skillinfo.pkey = acdcallinformation.skillid 
    ) AS Skill, 
    SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time', 
    SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time' 
FROM `acdcallinformation` acdcallinformation 
WHERE DATEOFCALL = DATE(NOW()) 
GROUP BY skill; 

雖然,它可能更容易使用消耗此數據...只是一個想法的語言那些NULL的只有我轉換爲零。

而且,我的文檔爲COUNT的閱讀讓我覺得它永遠不會返回NULL,即:

SELECT 
    (SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) FROM acdcallinformation ag 
     WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL 
     AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid 
) AS 'Lost Calls', 
    COUNT(acdcallinformation.idleonqueue) AS 'Total Calls', 
    COUNT(acdcallinformation.`ANSWERTIME`) AS 'Answered', 
    (
     SELECT 
      skillinfo.skillname 
     FROM skillinfo 
     WHERE skillinfo.pkey = acdcallinformation.skillid 
    ) AS Skill, 
    SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time', 
    SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time' 
FROM `acdcallinformation` acdcallinformation 
WHERE DATEOFCALL = DATE(NOW()) 
GROUP BY skill; 

最後,我想你可以用JOIN

SELECT 
     IFNULL(
     (SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) FROM acdcallinformation ag 
      WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL 
      AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid 
     ), 0) AS 'Lost Calls', 
     IFNULL(COUNT(acdcallinformation.idleonqueue), 0) AS 'Total Calls', 
     IFNULL(COUNT(acdcallinformation.`ANSWERTIME`),0) AS 'Answered', 
     skillinfo.skillname AS Skill, 
     SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time', 
     SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time' 
    FROM `acdcallinformation` acdcallinformation 
     INNER JOIN skillinfo ON skillinfo.pkey = acdcallinformation.skillid 
    WHERE DATEOFCALL = DATE(NOW()) 
    GROUP BY skill; 
處理你的第二個查詢
2

試試這個查詢。整個查詢只是一個猜測,但如果你提供了一些數據,它會被打擊。另外我用id作爲主鍵,你需要用你自己的密鑰替換它。避免使用子查詢來代替使用連接,它們要快得多。這是查詢。

SELECT 
    IF(l.LDifference IS NULL,0,r.RDifference) AS 'Lost Calls', 
    IF(COUNT(acdcallinformation.idleonqueue) IS NULL , 0 , COUNT(acdcallinformation.idleonqueue))AS 'Total Calls', 
    IF(COUNT(acdcallinformation.`ANSWERTIME`) IS NULL,0,COUNT(acdcallinformation.`ANSWERTIME`))AS 'Answered', 
    (SELECT skillinfo.skillname FROM skillinfo WHERE skillinfo.pkey = acdcallinformation.skillid) AS Skill, 
    SEC_TO_TIME(AVG(TIME_TO_SEC(a.answertime)- TIME_TO_SEC(a.firstringonqueue))) AS 'Average Answer Time', 
    SEC_TO_TIME(AVG(TIME_TO_SEC(a.IDLEONQUEUE) - TIME_TO_SEC(a.answertime))) AS 'Average Talk Time' 
FROM acdcallinformation as a 
INNER JOIN( 
    SELECT 
     (COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`)) as `LDifference` 
    FROM acdcallinformation ag 
    WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid 
) as l ON l.id = a.id 
INNER JOIN( 
    SELECT (COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`)) as `RDifference` 
     FROM acdcallinformation ag 
    WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall= DATE(NOW()) AND ag.skillid = acdcallinformation.skillid 
) as r ON r.id = a.id 
WHERE a.DATEOFCALL = DATE(NOW()) 
GROUP BY skill; 
2

嘗試。 使用INNER JOIN,IF()並嘗試避免不必要的子查詢。

SELECT IFNULL(ag.skillcount, 0) AS 'Lost Calls', COUNT(info.idleonqueue) AS 'Total Calls', 
     COUNT(info.ANSWERTIME) AS 'Answered', si.skillname AS Skill, 
     SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time', 
     SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time' 
FROM acdcallinformation AS info 
INNER JOIN (
      SELECT skillid, COUNT(PKEY)-COUNT(ANSWERTIME) skillcount 
      FROM acdcallinformation 
      WHERE COMPLETED = 1 AND DATEofcall = DATE(NOW()) AND answertime IS NULL 
      ) ag ON ag.skillid = info.skillid 
INNER JOIN skillinfo si ON si.pkey = info.skillid 
WHERE DATEOFCALL = DATE(NOW()) 
GROUP BY si.skillname;