2013-12-11 39 views
1

我有以下的(故意非規範化用於演示目的)樣品CARS表:如何在MySQL中正確使用GROUP BY?

| CAR_ID | OWNER_ID | OWNER_NAME | COLOR | 
|--------|----------|------------|-------| 
|  1 |  1 |  John | White | 
|  2 |  1 |  John | Black | 
|  3 |  2 |  Mike | White | 
|  4 |  2 |  Mike | Black | 
|  5 |  2 |  Mike | Brown | 
|  6 |  3 |  Tony | White | 

如果我想計算每個車主的汽車的數量,返回此:

| OWNER_ID | OWNER_NAME | TOTAL | 
|----------|------------|-------| 
|  1 |  John |  2 | 
|  2 |  Mike |  3 | 
|  3 |  Tony |  1 | 

我知道我可以寫下面的查詢:

SELECT owner_id, owner_name, COUNT(*) total FROM cars 
GROUP BY owner_id, owner_name 

然而,從GROUP BY子句中刪除owner_name給了我相同的結果。

  1. 這兩個查詢有什麼區別?
  2. 在什麼情況下,我應該使用SELECT聲明中的所有非累計字段進行分組,並且哪些不應該?
  3. 您可以舉一個例子,說明在刪除非聚合字段時,該分組會返回不同的結果並解釋原因?
+0

downvote的解釋將有助於澄清問題... –

回答

2

首先要明確的是SQL不是MySQL。

在標準SQL中,不允許按非聚合字段的子集進行分組。原因很簡單。假設我運行此查詢:

SELECT color, owner_name, COUNT(*) FROM cars 
GROUP BY color 

該查詢沒有任何意義。即使試圖解釋它也是不可能的。確定它是選擇顏色並計算每種顏色的汽車數量。但是,它也添加了owner_name字段,並且可以有多個給定顏色的所有者,因爲它是White顏色的情況。因此,如果單個color的值可能有多個owner_name,這恰好是GROUP BY子句中的唯一字段......那麼將返回哪個owner_name

如果需要返回owner_name,則應添加某種標準以僅選擇其中的一個標準,例如按字母順序選擇其中的一個,在本例中爲John。該標準將導致添加一個聚合函數MIN(owner_name),然後查詢將再次變得有意義,因爲它將按照select語句中的所有非聚集字段進行分組。

正如您所看到的,標準SQL在分組中非常不靈活是一個明顯的實際原因。如果不是這樣,您可能會遇到一些尷尬的情況,即某列的值不可預知,這並不是一個好詞,特別是當查詢正在運行時向您顯示您的銀行賬戶交易。

話雖如此,那爲什麼MySQL允許查詢可能沒有意義?更糟糕的是,上述查詢中的錯誤可能只是語法檢測!簡短的答案是:表現。長時間的回答是,在某些情況下,根據數據關係,從該組獲得不可預知的價值將導致可預測的價值。

如果你還沒有意識到這一點的是,在其中你可以預測你會採取一個不可預知的元素從一組會如果組中的所有元素都是相同獲取的價值的唯一途徑。這種情況的一個明顯例子就是在同一個問題中的示例查詢。請看錶格中的owner_idowner_name。很明顯,給定任何owner_id,例如2,你只能有一個不同的owner_name。即使有很多行,通過選擇任何行,您將得到Mike作爲結果。在正式的數據庫術語中,這可以解釋爲owner_id在功能上確定owner_name

讓我們來仔細看看,充分工作MySQL查詢:

SELECT owner_id, owner_name, COUNT(*) total FROM cars 
GROUP BY owner_id 

鑑於任何owner_id這將返回相同的owner_name,所以將它添加到GROUP BY條款不會導致更多的行返回。即使添加聚合函數MAX(owner_name)也不會導致返回更少的行。結果數據將完全相同。在這兩種情況下,查詢都會立即變成一個合法的標準SQL查詢,因爲至少所有的非聚合字段都會被分組。所以有三種方法可以獲得相同的結果。

但是,正如我之前提到的,這種非標準分組具有性能優勢。您可以檢查此so underrated link在這樣做是爲了更詳細的說明,但我要舉的最重要的部分:

您可以使用此功能,避免不必要的列排序和分組,以獲得更好的性能。 [...]服務器可以自由選擇每個組中的任何值,因此除非它們相同,否則所選值是不確定的。

有一兩件事值得一提的是,結果不一定錯而是不確定。換句話說,獲得預期的結果並不意味着你已經寫出了正確的查詢。編寫正確的查詢將始終爲您提供預期的結果。

如您所見,可能值得將這個MySQL擴展應用於GROUP BY子句。無論如何,如果這尚未完全清除,那麼有一條經驗法則可以確保您的分組總是正確的:總是至少由選擇子句中的所有非聚合字段進行分組。在某些情況下,您可能會浪費幾個CPU週期,但這比返回不確定的結果要好。如果您仍然對沒有正確分組感到恐懼,那麼更改ONLY_FULL_GROUP_BY SQL模式可能是最後的手段:)

可能您的分組是正確的和高性能的......或者至少是正確的。