2011-06-23 225 views
5

我是一個MySQL查詢noobie,所以我確信這是一個明顯的答案。MySQL Group By和HAVING

但是,我正在看這兩個查詢。他們會返回不同的結果集?我知道排序過程會以不同的方式開始,但我相信他們會返回相同的結果,而第一個查詢的效率稍高一點。

查詢1:HAVING,然後與

SELECT user_id 
FROM forum_posts 
GROUP BY user_id 
    HAVING COUNT(id) >= 100 
    AND user_id NOT IN (SELECT user_id FROM banned_users) 

查詢2:式中,然後將具有

SELECT user_id 
FROM forum_posts 
WHERE user_id NOT IN(SELECT user_id FROM banned_users) 
GROUP BY user_id 
    HAVING COUNT(id) >= 100 

回答

1

實際上所述第一查詢的效率會降低(施加WHEREHAVING)。
UPDATE

一些僞代碼來說明如何你的查詢執行([十分]的簡化版本)。
第一個查詢:
1. SELECT user_id FROM forum_posts
2. SELECT user_id FROM banned_user
第3組,計數等
4.排除記錄從當它們存在在第二

第二查詢
呈現設定第一結果 1. SELECT user_id FROM forum_posts
2. SELECT user_id FROM banned_user
3.如果它們在第二
呈現設定的第一結果排除記錄4.組,計數等

步驟1,2的順序並不重要,mysql可以選擇任何它認爲更好的。重要的區別在於步驟3,4。申請後GROUP BY。分組通常比加入費用更高(不包括記錄在本例中可以考慮爲加入操作),因此分組所需的記錄越少,性能就越好。

+0

太好了,謝謝!有趣的是,我將不得不運行一些測試。我認爲效率更高,因爲在分組之後比較NOT IN banned_users部分的記錄比較少,如果這是有道理的。 – kimmothy

+0

是的,該條件將僅在分組之前根據結果分組進行測試,而不是全部分組。 – aorcsik

+0

@ kimmothy:'NOT IN'中的子查詢確實只需要執行一次。 – a1ex07

0

HAVING條件適用於按結果分組的,並且由於您按user_id進行分組,因此它們的所有可能值都將出現在分組結果中,因此放置user_id條件並不重要。

+0

配售很重要。如果應用了'WHERE',分組將在更少的行中完成(甚至爲零),所以COUNT()必須僅針對那些行進行計算。如果它留給'HAVING'子句,則對所有行進行分組(和計數),然後檢查條件。結果:如果禁止的用戶在所有用戶中所佔的比例很大,則差異是速度會很快(按比例) –

+0

當然,只有在優化程序針對2個查詢產生不同計劃時纔會出現速度差異。 –

+0

非常感謝您的點贊,我從這裏的答案中學到了很多東西。 :) – aorcsik

-1

不,它沒有給出相同的結果。

因爲第一查詢將從計數(ID)的條件下篩選記錄

另一查詢過濾器記錄,然後施加具有子句。

第二個查詢是正確寫入

+2

由於您所說的結果會有所不同,因此在您知道哪些問題正在得到解決之前,您幾乎不可能聲稱哪一個是正確編寫的。至少,它們都是正確的*語法*。事實上,結果也是一樣的。它們的效率會有所不同。 –

+0

@Andriy:你確定效率有差別嗎? –

+0

@ypercube:我期望HAVING在WHERE之後進行評估,事實上,即使在GROUP BY之後(我認爲這也是在WHERE之後計算的)。因此,第一個查詢將不必要地計算以後根據'user_id'丟棄的行數。第二個在聚合之前在'user_id'上過濾掉。 –

0

對我來說,第二個查詢更有效,因爲它降低了對GROUP BY和HAVING記錄數。

或者,您可以嘗試下面的查詢,以避免使用IN:

SELECT `fp`.`user_id` 
FROM `forum_posts` `fp` 
LEFT JOIN `banned_users` `bu` ON `fp`.`user_id` = `bu`.`user_id` 
WHERE `bu`.`user_id` IS NULL 
GROUP BY `fp`.`user_id` 
HAVING COUNT(`fp`.`id`) >= 100 

希望這有助於。

0

您已經回答了這兩個查詢會顯示相同的結果以及其中一個更有效的各種意見。

我opininion是將有效率(速度)一個差,僅當優化產率與不同的計劃爲2次的查詢。我認爲,對於最新的MySQL版本,優化器足夠聰明,可以爲任一查詢找到相同的計劃,因此在所有上都沒有差別,但是當然可以使用EXPLAIN測試並查看執行計劃或運行2個查詢針對一些測試表。

我會在任何情況下使用第二個版本,只是爲了安全起見。


讓我補充說:

  • COUNT(*)通常比在MySQL COUNT(notNullableField)更有效。在未來MySQL版本修復之前,請在適用的地方使用COUNT(*)

因此,您還可以使用:

SELECT user_id 
FROM forum_posts 
WHERE user_id NOT IN 
    (SELECT user_id FROM banned_users) 
GROUP BY user_id 
HAVING COUNT(*) >= 100 
  • 也有其他的方式申請GROUP BY之前達到同樣的(以NOT IN)分結果。

使用LEFT JOIN/NULL

SELECT fp.user_id 
FROM forum_posts AS fp 
    LEFT JOIN banned_users AS bu 
    ON bu.user_id = fp.user_id 
WHERE bu.user_id IS NULL 
GROUP BY fp.user_id 
HAVING COUNT(*) >= 100 

使用NOT EXISTS

SELECT fp.user_id 
FROM forum_posts AS fp 
WHERE NOT EXISTS 
    (SELECT * 
    FROM banned_users AS bu 
    WHERE bu.user_id = fp.user_id 
) 
GROUP BY fp.user_id 
HAVING COUNT(*) >= 100 

其中的3種方法比較快取決於你的表的大小和很多其他因素的影響,所以最好是測試與你的數據。