2011-05-24 307 views
6

正確我不知道爲什麼,但這個查詢需要超過6秒的執行,索引的所有設置都正確,如果我分別運行每個查詢它執行很好,執行時間少於0.5秒。緩慢的MYSQL查詢與使用COUNT的子查詢

下面是該查詢

SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name, 
(SELECT COUNT(*) 
    FROM supplier_questions q1 
    WHERE c.supplier_id = q1.supplier_id AND q1.incomplete = '0') AS questions, 
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated, 
(SELECT COUNT(*) 
    FROM supplier_questions q2 
    WHERE c.supplier_id = q2.supplier_id AND q2.reviewed = '1') AS reviewed, 
questapproved, 
ss.supplier_no AS supplier_no 
FROM suppliers c 
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id 
WHERE c.supplier_id != '0' AND ss.site_id = '2' 
GROUP BY c.supplier_id 
ORDER BY c.supplier_name ASC 
LIMIT 0, 20 

的解釋查詢的結果是如下

id select_type table type possible_keys key key_len ref rows Extra 
1 PRIMARY ss ref site_id,supplier_id site_id 4 const 1287 Using where; Using temporary; Using filesort 
1 PRIMARY c eq_ref PRIMARY PRIMARY 4 ss.supplier_id 1 
3 DEPENDENT SUBQUERY q2 ref supplier_id,reviewed reviewed 4 const 263 Using where 
2 DEPENDENT SUBQUERY q1 ref supplier_id,incomplete incomplete 4 const 254 Using where 

原因計數查詢是在那裏,是因爲我需要知道那些行數表,這是不能在另一個查詢中完成,因爲結果還需要按這些值排序:(

+0

你能確認c.supplier_name上有一個索引嗎?當你刪除兩個從屬子查詢時,解釋是什麼? – 2011-05-24 10:42:11

+1

請發表CREATE TABLE? – borrel 2011-05-24 10:42:16

+0

supplier_name上沒有索引,但添加後沒有更改。 – Neo 2011-05-24 10:51:35

回答

2

作爲在黑暗中的刺,它運行得更快嗎?(我沒有得到一個MySQL來驗證Y開,所以請原諒任何輕微的錯誤,但你可能會得到的想法)

SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name, questions, reviewed 
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated, 
questapproved, ss.supplier_no AS supplier_no 
FROM suppliers c 
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id 
inner join 
(SELECT supplier_id, sum(if(incomplete='0',1,0)) as questions, sum(if(incomplete='1',1,0)) as reviewed FROM supplier_questions q1 group by supplier_id) as tmp 
on c.supplier_id = tmp.supplier_id 
WHERE c.supplier_id != '0' AND ss.site_id = '2' 
GROUP BY c.supplier_id 
ORDER BY c.supplier_name ASC LIMIT 0, 20 
+0

它不會,因爲他最終會將整個表加入到一起。他的查詢計劃中的奇怪之處在於它沒有使用c.supplier_name獲取前20行,並將join/subqueries應用於該行。 – 2011-05-24 10:49:25

+0

運行得更快,但只返回3行而不是可能的1000+行 – Neo 2011-05-24 10:49:35

+0

您可能不想要一個內部聯接,您可能只想要一個聯接,因爲很明顯,它只包括兩個表中的項目。 @Denis ,這沒有幫助,我只是想知道如果做一次問題和評論的次數會有什麼顯着差異。 – BugFinder 2011-05-24 10:54:59

1

如果刪除的語法子選擇你最終的東西是這樣的:

SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name, 
COUNT(IF (q1.incomplete = '0', '0', null)) AS questions, 
IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated, 
COUNT(IF (q1.reviewed = '1', '1', null)) AS reviewed, 
questapproved, 
ss.supplier_no AS supplier_no 
FROM suppliers c 
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id 
LEFT OUTER JOIN supplier_questions q1 ON c.supplier_id = q1.supplier_id 
WHERE c.supplier_id != '0' AND ss.site_id = '2' 
GROUP BY c.supplier_id 
ORDER BY c.supplier_name ASC 
LIMIT 0, 20 

我不沒有MySQL數據庫可用,所以可能在我的SQL中有錯誤。 這個想法是刪除子查詢並將其替換爲外連接 並使用IF來只計數相關行。

+0

需要3.6秒運行 – Neo 2011-05-24 11:15:26

2
FROM suppliers c 
INNER JOIN supplier_site ss ON c.supplier_id = ss.supplier_id 
WHERE c.supplier_id != '0' AND ss.site_id = '2' 
GROUP BY c.supplier_id 
ORDER BY c.supplier_name ASC 

由於自動生成的主鍵永遠不會等於0(除非大數據庫設計錯誤),您可以刪除c.supplier_id!='0'子句。

爲了提高可讀性,ss.site_id ='2'應該處於連接條件。

它看起來應該只匹配每個供應商的表supplier_site中的一行(如果這是您通常的1-N事物地址關係,即您選擇每個供應商的第二個地址,也許'2'對應於'帳單地址'或其他),所以GROUP BY c.supplier_id是無用的。如果GROUP BY實際上做了某事,那麼查詢是錯誤的,因爲「地址」列可能來自supplier_site表,它來自隨機行。

因此,這裏的簡化FROM(在WHERE消失):

FROM suppliers c 
INNER JOIN supplier_site ss ON 
    (c.supplier_id = ss.supplier_id AND ss.site_id = '2') 
ORDER BY c.supplier_name ASC 

我想你上了車c.supplier_name一個索引,查詢的這部分應該是非常快的。

現在試試這個查詢:

SELECT a.*, 
    questapproved, 
    ss.supplier_no AS supplier_no, 
    IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated, 
    sum(q.incomplete = '0') AS questions, 
    sum(q.reviewed = '1') AS reviewed 
FROM 
(
    SELECT c.supplier_id, supplier_name, address1, address2, address3, address4, suppliertype, postcode, contact_name 
    FROM suppliers c 
    INNER JOIN supplier_site ss ON 
     (c.supplier_id = ss.supplier_id AND ss.site_id = '2') 
    ORDER BY c.supplier_name ASC 
    LIMIT 0, 20 
) a 
LEFT JOIN supplier_questions q ON (q.supplier_id = c.supplier_id) 
GROUP BY c.supplier_id 
ORDER BY c.supplier_name; 
+0

產生了一些SQL錯誤,移動questapprove,supplier_no和評級到FROM查詢後,它抱怨avout c.supplier_id是ON子句中的未知列 – Neo 2011-05-24 11:18:19

+0

讓你的查詢工作,它是很多更快,但不能按順序排列,因爲它不會因爲某種原因訂購 – Neo 2011-05-24 11:21:43

0

我會先通過查詢預聚集體通過供應商的問題數和經審查後嘗試重組。然後,加入其餘的細節。通過使用STRAIGHT_JOIN關鍵字,它應該按照顯示的順序進行處理。這將首先進行預先彙總,然後使用THAT作爲參加回供應商和供應商網站的依據。無論是否需要外部組織,因爲它基於供應商ID。然而,加入supplier_sites(您的ss.supplier_no)意味着供應商擁有多個位置。這是否意味着地址和活動狀態列是源自該表的?

問題連接是否應與特定供應商相關聯,並且是否與相應的網站位置相關?

此外,由於prequery在supplier_id!='0'上有WHERE子句,因此它不需要向下流,因爲這將成爲正常連接到其他表的基礎,從而將它們排除在結果集之外。

SELECT STRAIGHT_JOIN 
     PreAggregate.supplier_id, 
     PreAggregate.supplier_name, 
     address1, 
     address2, 
     address3, 
     address4, 
     suppliertype, 
     postcode, 
     contact_name, 
     PreAggregate.Questions, 
     IF (active=1,'Yes', IF (active=2, 'NCR Only','Inactive')) AS rated, 
     PreAggregate.Reviewed, 
     questapproved, 
     ss.supplier_no AS supplier_no 
    FROM 
     (select 
      s1.Supplier_ID, 
      s1.Supplier_Name, 
      SUM(IF(q1.Incomplete = '0', 1, 0)) Questions, 
      SUM(IF(q1.Reviewed = '1', 1, 0)) Reviewed 
      from 
      suppliers s1 
       join supplier_questions q1 
        ON s1.supplier_id = q1.supplier_id 
      where 
      s1.supplier_id != '0' 
      group by 
      s1.Supplier_ID 
      ORDER BY 
      s1.supplier_name ASC) PreAggregate 

     JOIN suppliers c 
      ON PreAggregate.Supplier_ID = c.Supplier_ID 

     JOIN supplier_site ss 
      ON PreAggregate.Supplier_ID = ss.supplier_id 
      AND ss.Site_ID = '2' 
    LIMIT 0, 20 
+0

單個供應商可以提供mult用戶只允許訪問某些網站,因此supplier_sites,與查詢有關,它返回0行 – Neo 2011-05-24 11:39:55

+0

@Neo,我將LIMIT 0,20移動到查詢的OUTER部分...我猜內部查詢限於20個,其中,沒有合格的供應商網站的site_id ='2'... – DRapp 2011-05-24 11:46:43