2012-05-21 316 views
1

哪個查詢將執行得更快,哪個查詢是完美的?哪個MySQL查詢速度更快?

SELECT 
    COUNT(*) AS count 
FROM 
    students 
WHERE 
    status = 1 
AND 
    classes_id IN(
        SELECT 
         id 
        FROM 
         classes 
        WHERE 
         departments_id = 1 
       ); 

或者

SELECT 
    COUNT(*) AS count 
FROM 
    students s 
LEFT JOIN 
    classes c 
ON 
    c.id = s.classes_id 
WHERE 
    status = 1 
AND 
    c.departments_id = 1 

,我兩次都查詢將輸出相同的結果。現在我想知道哪種方法會更快執行,哪種方法是正確的方法?

+6

當您測試時哪個速度更快? – symcbean

+1

使用'EXPLAIN'運行並自己查看。例子:'解釋select count(*)...' –

+0

解釋你會得到答案 –

回答

5

您應該始終使用EXPLAIN來確定您的查詢將如何運行。

不幸的是,MySQL將執行你的子查詢作爲一個依賴查詢,這意味着子查詢將在外部查詢中的每一行運行。你會認爲MySQL會足夠聰明地檢測到子查詢不是一個相關的子查詢,只會運行一次,唉,它還不夠聰明。因此,MySQL將掃描學生中的所有行,爲每一行運行子查詢,而不使用任何外部查詢上的任何索引。

編寫查詢作爲JOIN將允許MySQL的利用索引,下面的查詢將是最佳的方式來寫它:

SELECT COUNT(*) AS count 
FROMstudents s 
JOIN classes c 
    ON c.id = s.classes_id 
    AND c.departments_id = 1 
WHERE s.status = 1 

這將採用以下指標:

students(`status`) 
classes(`id`, `departements_id`) : multi-column index 
0
兩個查詢

運行EXPLAIN SELECT ...和檢查哪一個做什麼;)

3

從設計和清晰的角度我會避免內選擇喜歡的第一個。的確,如果能夠100%確定每個查詢是否優化以及如何進行優化以及哪個優化運行需要查看您使用的SQL服務器如何將它與它的計劃進行互操作,那麼這是事實。在Mysql中,使用「解釋」。

但是....即使沒有看到這個,我的錢仍然只在Join only版本中...內部選擇版本必須在完全執行內部選擇之後才能確定在「IN」中使用的值,子句 - 我知道這是真實的,當你在函數中包裝的東西,並確定它是真實的,當堅持一個選擇作爲IN論據。我也知道這是一個很好的方法,可以完全消除你在內部select中的表上對索引可能有的任何好處。

我一般認爲,內部選擇只有非常罕見的查詢情況才真正需要。通常情況下,那些誰經常使用他們正在考慮像傳統的迭代流程序員在關係數據庫的結果集而言不是真的想...

+0

tanx 4你的回覆...尋找更多來自mysql專家的回覆.... –

2

EXPLAIN無論是查詢單獨

兩個查詢之間的差額,撒哈拉的查詢與連接

大部分聯接比子查詢快。 Join創建執行計劃並預測要處理的數據,因此可節省時間。另一方面,子查詢運行所有查詢,直到加載所有數據。大多數開發人員使用子查詢,因爲它們比JOINS更具可讀性,但在性能問題上,JOIN是更好的解決方案。

+0

謝謝@Moyed Ansari ...等待其他專家回覆..... –

+0

感謝您的回覆:D –

2

要找出最好的方法是測量它:

沒有索引

  • 查詢1:0.9S
  • 問題2:0.9S

隨着指數

  • 查詢1:0.4秒
  • 問題2:0.2秒

結論是:

  • 如果您沒有索引,那麼使用哪個查詢並沒有什麼不同。
  • 如果您有正確的索引,聯接速度會更快。
  • 添加正確索引的效果大於選擇正確查詢的效果。如果性能很重要,請確保您擁有正確的索引。

當然,您的結果可能因MySQL版本和您擁有的數據分佈而異。

以下是我測試過它:

  • 百萬學生(25%的狀態1)。
  • 50,000門課程。
  • 10個部門。

這是我用來創建測試數據的SQL:

CREATE TABLE students 
(id INT PRIMARY KEY AUTO_INCREMENT, 
status int NOT NULL, 
classes_id int NOT NULL); 

CREATE TABLE classes 
(id INT PRIMARY KEY AUTO_INCREMENT, 
departments_id INT NOT NULL); 

CREATE TABLE numbers(id INT PRIMARY KEY AUTO_INCREMENT); 

INSERT INTO numbers VALUES(),(),(),(),(),(),(),(),(),(); 

INSERT INTO numbers 
SELECT NULL 
FROM numbers AS n1 
CROSS JOIN numbers AS n2 
CROSS JOIN numbers AS n3 
CROSS JOIN numbers AS n4 
CROSS JOIN numbers AS n5 
CROSS JOIN numbers AS n6; 

INSERT INTO classes (departments_id) 
SELECT id % 10 FROM numbers WHERE id <= 50000; 

INSERT INTO students (status, classes_id) 
SELECT id % 4 = 0, id % 50000 + 1 FROM numbers WHERE id <= 1000000; 

SELECT COUNT(*) AS count 
FROM students 
WHERE status = 1 
AND classes_id IN (SELECT id FROM classes WHERE departments_id = 1); 

SELECT COUNT(*) AS count 
FROM students s 
LEFT JOIN classes c 
ON c.id = s.classes_id 
WHERE status = 1 
AND c.departments_id = 1; 

CREATE INDEX ix_students ON students(status, classes_id); 
1

兩個查詢不會產生相同的結果:

SELECT 
    COUNT(*) AS count 
FROM 
    students 
WHERE 
    status = 1 
AND 
    classes_id IN(
        SELECT 
         id 
        FROM 
         classes 
        WHERE 
         departments_id = 1 
       ); 

...將返回的數量學生表中有classes_id字段的行也在classes_id爲1的classes表中。

SELECT 
    COUNT(*) AS count 
FROM 
    students s 
LEFT JOIN 
    classes c 
ON 
    c.id = s.classes_id 
WHERE 
    status = 1 
AND 
    c.departments_id = 1 

...將返回狀態字段爲1且可能超過該值的學生表中的總行數,具體取決於您的數據組織方式。

如果您希望查詢返回相同的內容,則需要將LEFT JOIN更改爲INNER JOIN,以便它僅匹配適合這兩個條件的行。

+0

這是正確的。碰巧這兩個查詢將在OP的情況下返回相同的結果,因爲我假定所有'students.classes_id'引用'classes.id'。但在一般情況下,這兩個查詢在語義上並不完全相同,因此MySQL無法以相同的方式優化它們。 – RandomSeed