2016-11-07 152 views
0

我幾個月前寫了這個查詢。它運行良好。但是,這個查詢一天比一天要慢。爲什麼這個查詢需要這麼長時間來執行

此查詢檢查除了同一個表執行以外的多個表中的帳單歷史記錄。

下面是該查詢 -

SELECT * FROM (
      SELECT *,     
      (SELECT username FROM users WHERE id = u_bills.UserId) AS username, 
      (SELECT first_name FROM users WHERE id = u_bills.UserId) AS first_name, 
      (SELECT last_name FROM users WHERE id = u_bills.UserId) AS last_name, 
      (SELECT phone FROM users WHERE id = u_bills.UserId) AS phone, 
      (SELECT email FROM users WHERE id = u_bills.UserId) AS email, 
      (SELECT CPRate FROM cpt WHERE UserId = u_bills.UserId ORDER BY AddedDate DESC LIMIT 0,1) AS cprate, 
      (SELECT (SELECT PopName FROM pops WHERE PopId = p.PopName) AS PopFullName FROM u_setupinfos AS p 
      WHERE UserId = u_bills.UserId) AS popname, 
      (SELECT active FROM users WHERE id = u_bills.UserId) AS active,     
      (SELECT SUM(PaidAmount) AS PaidAmount FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate)) AS PaidAmount, 
      (SELECT PaidDate FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate) 
      GROUP BY MONTH(PaidDate)) AS PaidDate, 
      (SELECT PaymentMedia FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate) 
      GROUP BY MONTH(PaidDate)) AS PaymentMedia, 
    (SELECT TransactionId FROM u_billhistory 
      WHERE UserId = u_bills.UserId AND MONTH(AddedDate) = MONTH(PaidDate) GROUP BY MONTH(PaidDate)) AS TransactionId, 
      (SELECT GROUP_CONCAT(CONCAT(`BillHisId`,';',`PaidAmount`,';',`PaidDate`) separator '|') AS vals 
FROM u_billhistory 
WHERE UserId = u_bills.UserId 
AND YEAR(PaidDate) = YEAR(CURDATE())) AS TotalPaids 
      FROM u_bills) AS m 
     WHERE m.username = 'abc' 

請查閱圖像 -

enter image description here

我想組織此查詢的EXPLAIN報告。我需要建議使這個查詢更快,例如3到5秒,而不是1分半鐘或更長。我在這個查詢中犯了什麼錯誤?

重要筆記記錄

我使用的查詢索引幾乎每一列

+0

也許沒有 - 您是否更新過服務器上的統計信息?那段時間你的數據如何增長?還有其他數據庫爭奪資源嗎? –

+4

這就是說 - 爲什麼所有的子查詢一次只能得到一個字段? –

+2

,因爲它的巨大與subquerys –

回答

5

,因爲你讓運行一個單獨的查詢到一個額外的表在結果每條記錄的每個字段它很慢...有時不止一個。您應該使用JOIN而不是嵌套的SELECT。對於某些分組結果,可以加入SELECT聲明,該聲明具有自己的GROUP BY。


此外,在各列單獨的索引不幫!

想象索引像電話簿。一個簡單的電話簿在Last Name和First Name上使用聚簇索引。然後它還列出了每個項目的地址和電話號碼。這就是聚集索引的含義:只是存儲數據的順序。

其他索引就像補充。 「電話號碼」上的非聚集索引就像書本末尾的一個補充,按順序列出電話號碼,然後顯示姓氏和名字(但不是地址)。 「地址」的索引也會這樣做:一個單獨的補充程序,按順序列出地址,然後給出名稱。

有了這些索引的補充,如果你有一個電話號碼並且想知道地址,你必須首先在補充中查找電話號碼以找到名字,然後在主要書籍中查找名字以獲得地址。在這個例子中,手機補充幫助,因爲它比電話簿的全面掃描更好。

但是,這裏要注意的重要一點是地址補充_does 而不是有助於此搜索,即使地址是您想要查找的確切字段。它可能可用於其他搜索,但它在這種情況下不起作用。此外,請記住,每次更新您的圖書需要您不僅更新圖書,還更新所有補充圖書。這不僅僅是存儲空間,而是更新每個索引的時間。你需要考慮是否值得。如果你有一堆未被使用的索引,沒有它們你最好。


再回到您的特定問題,關於的唯一指標,這將真正幫助你u_bills.usernameuser.UserId(希望的爲它的表簇)和兩個u_billhistory.UserIdu_billhistory.PaidDate單一指標。

這裏是在重構這個查詢的初步嘗試:

SELECT ub.*, u.username, u.first_name, u.last_name, u.phone, u.email, u.active, 
    m.PaidAmount, m.PaymentMedia, m.TransactionId, 
    y.TotalPaids, p.PopName As PopFullName, cpt2.CPRate  

FROM u_bills ub 
INNER JOIN users u on u.id = ub.UserId 
INNER JOIN u_setupinfos usi on usi.UserId = ub.UserID 
INNER JOIN pops p ON p.PopId = usi.PopName 
INNER JOIN (
    SELECT UserId, MIN(AddedDate) As cptDate 
    FROM cpt 
    GROUP BY UserID   
) cpt1 ON cp1.UserID = ub.UserID 
INNER JOIN (
    SELECT UserID, AddedDate, MIN(CPRate) AS CPRate 
    FROM cpt 
    GROUP BY UserID, AddedDate 
) cpt2 ON cpt2.UserID = ub.UserID AND cpt2.AddedDate = cpt1.ctpDate 
INNER JOIN (
    SELECT UserID, MONTH(PaidDate) PaidMonth, SUM(PaidAmount) AS PaidAmount, 
     PaymentMedia, TransactionId --these two fields need an aggregate function of some type! 
    FROM u_billhistory 
    GROUP BY UserId, MONTH(PaidDate) 
) m ON m.UserID = ub.UserId AND m.PaidMonth = MONTH(ub.AddedDate) 
INNER JOIN (
    SELECT UserID, YEAR(PaidDate) As PaidYear, 
     GROUP_CONCAT(CONCAT(`BillHisId`,';',`PaidAmount`,';',`PaidDate`) separator '|') AS TotalPaids 
    FROM u_billhistory 
    WHERE YEAR(PaidDate) = YEAR(CurDate()) 
    GROUP BY UserId, YEAR(PaidDate) 
) y ON Y.UserId = ub.UserId 
WHERE username='abc' 

請注意,這不是很完整,因爲我們不知道所有的關於您的架構和數據的信息,什麼似乎因爲有缺失GROUP BY和兩個缺失的聚合函數。 MySql這種方式很糟糕......一個更好的數據庫不會讓你的查詢運行,直到你修復這些東西。

+0

m.username有一個where子句所以那裏的索引會有所幫助。 – nicomp

+1

正在處理它..格式化使評估一些其他表需要更多的時間。 –

+0

@JoelCoehoorn感謝您的努力。不幸的是,它返回空。我知道編寫一個沒有模式的查詢很困難。如果你問,那麼我可以給你我的查詢輸出。希望這會有所幫助。我已經嘗試了幾個其他選擇你的查詢。 –

相關問題