2011-11-02 126 views
1

我有一個表現較慢的查詢。我知道使用從屬子查詢是不好的,但我想不出另一種獲取我想要的數據的方式。MySQL需要查詢優化

基本上,我想標記過去6個月至少有50張發票的客戶,但本月沒有發票。

這是我目前:

select 
    Customer.name, 
    Customer.id, 
    Customer.latitude, 
    Customer.longitude 
from 
    Customer 
where 
    EXISTS (
     SELECT 
      * 
     FROM 
      Invoice_Header 
     WHERE 
      Invoice_Header.inv_date BETWEEN '2011-03-02' AND '2011-10-02' 
     AND 
      Invoice_Header.account_number = Customer.account_number 
     HAVING COUNT(invoice_num) > 50 
    ) 
    AND NOT EXISTS (
     SELECT * 
     FROM 
      Invoice_Header 
     WHERE 
      InvHead.inv_date > '2011-10-02' 
     AND 
      InvHead.account_number = Customer.account_number 
    ) 
Group by name; 

客戶表中有大約12K的記錄,Invoice_Header有大約2mill記錄。

我有inv_date,account_number(在這兩個表中)的索引。

任何建議如何加快這一點,將不勝感激。

回答

2

我建議:

SELECT 
    c.name, 
    c.id, 
    c.latitude, 
    c.longitude 
FROM 
    Customer AS c 
    INNER JOIN (
     SELECT account_number, count(*) AS invoice_count 
     FROM Invoice_Header 
     WHERE inv_date >= '2011-03-02' AND inv_date <= '2011-10-02' 
     GROUP BY account_number 
    ) AS lsm 
     ON c.account_number = lsm.account_number 
    LEFT JOIN (
     SELECT account_number, count(*) AS invoice_count 
     FROM Invoice_Header 
     WHERE inv_date > '2011-10-02' 
     GROUP BY account_number 
    ) AS lm 
     ON c.account_number = lm.account_number 
    WHERE 
     lsm.invoice_count >= 50 
     AND IFNULL(lm.invoice_count, 0) = 0 
+0

+1這應該也適用。 –

1

嘗試運行您的查詢與explain並查看是否需要其他索引。

+0

這應該始終** **是第一步。 –

3

這應該消除相關子查詢是顯著更快:

SELECT c.name, c.id, c.latitude, c.longitude 
FROM Customer c 
INNER JOIN (
    SELECT account_number 
    FROM Invoice_Header ih 
    WHERE ih.inv_date BETWEEN '2011-03-02' AND '2011-10-02' 
    GROUP BY account_number 
    HAVING COUNT(*) > 50 
    MINUS 
    SELECT DISTINCT account_number 
    FROM Invoice_Header ih 
    WHERE ih.inv_date > '2011-10-02' 
) tbl 
ON tbl.account_number = c.account_number 
+0

+1打我吧 – Randy

+0

MySQL不支持MINUS - 所以Xint0的答案似乎工作得最好... – Tom

+0

是的,MySQL中沒有Minus。 – Imdad

2
select 
    C.name, 
    C.id, 
    C.latitude, 
    C.longitude, 
    I.account_number,   
    count(IF(I.inv_date>='2011-03-02' AND I.inv_date <='2011-10-02',I.inv_date,NULL)) as inv_count_6, 
    count(IF(I.inv_date > '2011-10-02',I.inv_date,NULL)) as inv_count_1 

from Customer C 
LEFT JOIN Invoice_Header I 
ON C.account_number = I.account_number 

GROUP BY C.id, I.account_number 
HAVING inv_count_6 >= 50 AND inv_count_1=0 
WHERE I.inv_date BETWEEN '2011-03-02' AND '2011-10-02' 

注:

1.發票是至少50如此條件是> = 50不> 50 。

2.You有索引添加到列inv_date

+0

我已更改查詢現在檢查 – Imdad

+1

我不認爲這可能有效。您的where子句對inv_date有兩個相互衝突的要求。它必須在兩個日期之間和最後一個日期之後,這是不可能的。所以這個查詢總是返回零結果。 –

+0

是的,它會工作100%。我已經更新了條件。覈實。 可能你對mysql的語法不甚瞭解。我認爲你對 count(IF(I.inv_date> ='2011-03-02'AND I.inv_date <='2011-10-02',I.inv_date,NULL))作爲inv_count_6有困惑, count(IF(I.inv_date>'2011-10-02',I.inv_date,NULL))inv_count_1 – Imdad