2013-04-10 87 views
4

以下查詢大約需要200秒才能完成。我試圖達到的目標是獲得6筆或更多付款的用戶,他們還沒有下訂單(不同市場有2個訂單表)。即使使用索引,MySQL聲明也非常緩慢

u.id,ju.id都是主鍵。

我已將user_idorder_status索引合併爲兩個訂單表中的一個索引。如果我在mp_orders表中刪除加入並加入COUNT(),則查詢需要8秒才能完成,但使用它需要很長時間。我想我已經索引了我可能擁有的每一件東西,但我不明白爲什麼需要很長時間才能完成。有任何想法嗎?

SELECT 
    u.id, 
    ju.name, 
    COUNT(p.id) as payment_count, 
    COUNT(o.id) as order_count, 
    COUNT(mi.id) as marketplace_order_count 
FROM users as u 
    INNER JOIN users2 as ju 
     ON u.id = ju.id 
    INNER JOIN payments as p 
     ON u.id = p.user_id 
    LEFT OUTER JOIN orders as o 
     ON u.id = o.user_id 
      AND o.order_status = 1 
    LEFT OUTER JOIN mp_orders as mi 
     ON u.id = mi.producer 
      AND mi.order_status = 1 
WHERE u.package != 1 
AND u.enabled = 1 
AND u.chart_ban = 0 
GROUP BY u.id 
HAVING COUNT(p.id) >= 6 
    AND COUNT(o.id) = 0 
    AND COUNT(mi.id) = 0 
LIMIT 10 

支付表

+-----------------+---------------+------+-----+---------+----------------+ 
| Field   | Type   | Null | Key | Default | Extra   | 
+-----------------+---------------+------+-----+---------+----------------+ 
| id    | bigint(255) | NO | PRI | NULL | auto_increment | 
| user_id   | bigint(255) | NO |  | NULL |    | 
+-----------------+---------------+------+-----+---------+----------------+ 

訂單表(mp_orders表幾乎相同的)

+-----------------+---------------+------+-----+---------+----------------+ 
| Field   | Type   | Null | Key | Default | Extra   | 
+-----------------+---------------+------+-----+---------+----------------+ 
| id    | int(255)  | NO | PRI | NULL | auto_increment | 
| order_number | varchar(1024) | NO | MUL | NULL |    | 
| user_id   | int(255)  | NO | MUL | NULL |    | 
+-----------------+---------------+------+-----+---------+----------------+ 
+0

你使用的是服務器端語言嗎? sql不能做數學運算來保存它的'生命'。即使使用php也是最好的。 – 2013-04-10 14:28:01

+0

@JoeCoderGuy是的我正在使用PHP網站,我也通過phpMyAdmin運行相同的SQL語句。 – Wasim 2013-04-10 14:29:17

+0

「EXPLAIN your_query_here」的完整輸出是什麼? – Jocelyn 2013-04-10 14:30:23

回答

4

您不需要COUNT您的訂單行,您需要檢索沒有訂單的用戶,這不是一回事。

相反計數,濾除沒有任何訂單的用戶:

SELECT 
    u.id, 
    ju.name, 
    COUNT(p.id) as payment_count 
FROM users as u 
    INNER JOIN users2 as ju 
     ON u.id = ju.id 
    INNER JOIN payments as p 
     ON u.id = p.user_id 
    LEFT OUTER JOIN orders as o 
     ON u.id = o.user_id 
      AND o.order_status = 1 
    LEFT OUTER JOIN mp_orders as mi 
     ON u.id = mi.producer 
      AND mi.order_status = 1 
WHERE u.package != 1 
AND u.enabled = 1 
AND u.chart_ban = 0 
AND o.id IS NULL -- filter happens here 
AND mi.id IS NULL -- and here 
GROUP BY u.id 
HAVING COUNT(p.id) >= 6 
LIMIT 10 

這將防止引擎計算每個訂單的每個用戶,而你將獲得大量的時間。

可以認爲引擎應該使用索引來計數,所以計數必須足夠快。
I will quote from a different site: InnoDB COUNT(id) - Why so slow?

這可能與緩衝做時,InnoDB不緩存索引呢 緩存到內存中的實際數據行,因爲這個東西 似乎是一個簡單的掃描它不加載主鍵索引但是所有的數據都進入RAM然後在其上運行你的查詢。這可能需要 一段時間的工作 - 希望如果你在這個 之後在同一張桌子上運行查詢,那麼他們將運行得更快。

MyISAM數據加載索引到RAM中,然後運行它的計算過 這個空間,然後返回一個結果,作爲一個指標一般是多少 比在表中的所有數據,你會看到有一個 直接的影響要小得多。

另一種選擇可能是InnoDB的存儲磁盤 上的數據的方式 - InnoDB的文件是一個虛擬的表空間,因此不被數據表格必然下令,如果你有一個 零散的數據文件,然後這可能會爲您的磁盤IO創建問題,結果運行速度會變慢。 MyIsam通常是 順序文件,因此如果您使用索引訪問數據 系統完全知道該行所在的磁盤上的位置 - 您對innodb沒有這種奢侈,但我不認爲這是 特定問題進場只是一個簡單的COUNT(*) ==================== http://dev.mysql.com/doc/refman/5.0/en/innodb-restrictions.html 解釋這一點:

的InnoDB不保留內部表中的行數。 (在 的實踐中,由於多版本化,這會有些複雜。) 要處理SELECT語句(*)FROM t語句,InnoDB必須掃描表的索引 ,如果索引不完全 在緩衝池中。要快速計數,您必須使用自己創建的計數器 表,並讓應用程序根據 更新它,以便進行插入和刪除操作。如果您的表經常不會更改 ,則使用MySQL查詢緩存是一個很好的解決方案。 SHOW TABLE 如果近似行數足夠,STATUS也可以使用。請參閱 第14.2.11節「InnoDB性能調整技巧」。它實際上確實解釋了差異 - MyISAM理解COUNT(ID),其中ID是PK列 與COUNT(*)相同, ),其中MyISAM保持預先計算,而InnoDB 沒有。

+0

哇,你給的第一個查詢已經減少了0.3秒的時間,這是令人難以置信的!我可以理解爲什麼這會更快,我無法理解爲什麼我的查詢太慢了! – Wasim 2013-04-10 14:49:21

+0

這很明顯,如果你有很多訂單,發動機必須爲每個用戶計算每個該死的訂單,然後再比較爲零。因此,您可以完整掃描每個訂單表。而用你的左連接和索引,確定用戶是否有輕微的訂單。 – 2013-04-10 14:51:40

+0

對我現在明白了,非常感謝您的幫助。很好的回答 – Wasim 2013-04-10 14:54:39

3

嘗試取出COUNT() = 0IS NULL檢查代替:

SELECT 
    u.id, 
    ju.name, 
    COUNT(p.id) as payment_count, 
    0 as order_count, 
    0 as marketplace_order_count 
FROM users as u 
    INNER JOIN users2 as ju 
     ON u.id = ju.id 
    INNER JOIN payments as p 
     ON u.id = p.user_id 
    LEFT OUTER JOIN orders as o 
     ON u.id = o.user_id 
     AND o.order_status = 1 
    LEFT OUTER JOIN mp_orders as mi 
     ON u.id = mi.producer 
     AND mi.order_status = 1 
WHERE 
    u.package != 1 
AND u.enabled = 1 
AND u.chart_ban = 0 
AND mi.id IS NULL 
AND o.id IS NULL 
GROUP BY u.id 
HAVING COUNT(p.id) >= 6 
LIMIT 10 

但我認爲8秒對於普通查詢仍然太多。您應該在沒有OUTER JOINS的情況下發布主查詢的解釋計劃以查看錯誤,例如包,啓用和圖表禁止過濾器可能會完全破壞它。