2010-12-16 56 views
0

我從來沒有在MySQL中使用IN的良好性能,並且我再次遇到了性能問題。將IN子查詢重寫爲JOIN

我試圖創建一個視圖。它的相關部分是:

SELECT 
    c.customer_id, 
    .... 
    IF (c.customer_id IN (
      SELECT cn.customer_id FROM customer_notes cn 
     ), 1, 0) AS has_notes 
    FROM customers c; 

基本上,我只是想知道客戶是否有附加的說明。多少筆記無關緊要。我如何使用JOIN來重寫它以加速它?

客戶表目前擁有150萬行,因此性能是一個問題。

+1

我想你想重寫它爲'EXISTS'查詢而不是'JOIN'。 – Gabe 2010-12-16 23:25:48

回答

1

你不需要客戶ID選擇?就目前而言,您是不是每個客戶都運行一次子查詢,並獲得一串真或假的值,而不知道哪一個適用於哪個客戶?

如果這是您所需要的,則不需要引用customers表(除非您保持數據庫處於語義分離狀態,並且customer_notes中可能存在沒有相應客戶的條目 - 但是你有比這個查詢的性能更大的問題);您可以簡單地使用:

SELECT DISTINCT Customer_ID 
    FROM Customer_Notes 
ORDER BY Customer_ID; 

獲取Customer_Notes表中至少有一個條目的客戶ID值列表。

如果你想客戶ID值和相關的真/假值的列表,那麼你需要做一個連接:

SELECT C.Customer_ID, 
     CASE WHEN N.Have_Notes IS NULL THEN 0 ELSE 1 END AS Has_Notes 
    FROM Customers AS C 
    LEFT JOIN (SELECT Customer_ID, COUNT(*) AS Have_Notes 
       FROM Customer_Notes 
       GROUP BY Customer_ID) AS N 
    ON C.Customer_ID = N.Customer_ID 
ORDER BY C.Customer_ID; 

如果這給表現不佳,檢查是否有Customer_Notes指數。客戶ID。如果這不是問題,請研究查詢計劃。


不能做......在視圖

對什麼是允許在視圖中始終處於任何DBMS一個令人討厭的小限制(MySQL的不單單是有限制)。但是,我們可以通過單個常規聯接來完成。我剛想起來。 COUNT(column)僅計算非空值,返回0,如果所有值都爲空,所以 - 如果你不介意的計數,而不是僅僅0或1 - 您可以使用:

SELECT C.Customer_ID, 
     COUNT(N.Customer_ID) AS Num_Notes 
    FROM Customers AS C 
    LEFT JOIN Customer_Notes AS N 
    ON C.Customer_ID = N.Customer_ID 
GROUP BY C.Customer_ID 
ORDER BY C.Customer_ID; 

如果你絕對必須有0或1:

SELECT C.Customer_ID, 
     CASE WHEN COUNT(N.Customer_ID) = 0 THEN 0 ELSE 1 END AS Has_Notes 
    FROM Customers AS C 
    LEFT JOIN Customer_Notes AS N 
    ON C.Customer_ID = N.Customer_ID 
GROUP BY C.Customer_ID 
ORDER BY C.Customer_ID; 

注意,使用「N.Customer_ID」是至關重要的 - 儘管表中的任何列會做(但你沒有泄露任何其他列,AFAICR名稱)和爲了清楚起見,我通常會使用除加入列之外的其他內容。

+0

嗨喬納森,是的,我選擇了客戶ID。我只是削減了SQL到最相關的部分。我編輯了我的問題,使其更清楚。 – 2010-12-16 23:46:52

+0

這是非常聰明的喬納森 - 首先將連接表分組,以確保連接語句只會從原始表中返回一行。不幸的是,我在MySQL中創建VIEW時無法使用子查詢,但我有辦法解決這個問題。在其上運行一些測試時,性能也似乎很好。 – 2010-12-17 00:31:59

1

我認爲EXISTS適合你的情況比JOININ更好。

SELECT 
    IF (EXISTS ( 
     SELECT * 
     FROM customer_notes cn 
     WHERE c.customer_id = cn.customer_id), 
     1, 0) AS filter_notes 
FROM customers 
+0

感謝您的建議Gabe。但是,這仍然給我可怕的表現。 – 2010-12-16 23:43:30

0

試試這個

SELECT 
    CASE WHEN cn.customer_id IS NOT NULL THEN 1 
     ELSE 0 
    END  AS filter_notes 
    FROM customers c LEFT JOIN customer_notes cn 
    ON c.customer_id= cn.customer_id 
+0

感謝您的建議,但如果客戶有兩張紙條,這兩行不會是行嗎? – 2010-12-16 23:44:05

+0

是的,它確實....如果你只有客戶出現一次,那麼你可以使用一個不同的查詢(選擇DISTINCT customer_id從customer_notes)cn而不是customer_notes cn ..當然它可能打擊性能comapared先前的查詢... ... – Chandu 2010-12-17 00:31:26