2014-05-23 57 views
4

我經常看到有人回答MySQL的問題與疑問是這樣的:用MySQL消除SELECT和HAVING /組間公共子BY子句

SELECT DAY(date), other columns 
FROM table 
GROUP BY DAY(date); 

SELECT somecolumn, COUNT(*) 
FROM table 
HAVING COUNT(*) > 1; 

我總是喜歡給列一個別名,指的是在GROUP BYHAVING子句,例如

SELECT DAY(date) AS day, other columns 
FROM table 
GROUP BY day; 

SELECT somecolumn, COUNT(*) AS c 
FROM table 
HAVING c > 1; 

是MySQL的足夠聰明,在後來的條款的表述都一樣SELECT,只有做一次?我不知道如何測試 - EXPLAIN沒有顯示任何區別,但它似乎沒有顯示它是如何進行分組或過濾的;它似乎主要用於優化連接和WHERE子句。

我傾向於對MySQL優化感到悲觀,所以我想給予我所有的幫助。

+0

萬一你需要一個直接的答案,作爲一個權宜之計,直到我可以找到一個權威性的參考(我懷疑它可能必須來自源代碼),我很確定解析器能夠識別調用確定性函數(甚至是表達式)並緩存結果以便在查詢中重用。 – eggyal

+0

+1一個連貫而有用的問題! –

+0

@eggyal我不需要任何東西,它更加好奇。我想知道是否應該在看到這種冗餘時建議人們改進他們的答案。我打算繼續以第二種方式編寫我的查詢,因爲我發現它們更具可讀性。 – Barmar

回答

5

我認爲這是可以使用睡眠()函數,
例如看看這個演示測試:http://sqlfiddle.com/#!2/0bc1b/1

Select * FROM t; 

| X | 
|---| 
| 1 | 
| 2 | 
| 2 | 

SELECT x+sleep(1) 
FROM t 
GROUP BY x+sleep(1); 

SELECT x+sleep(1) As name 
FROM t 
GROUP BY name; 

兩個查詢的執行時間爲3000毫秒(3秒)。
表中有3條記錄,對於每條記錄,查詢僅休眠1秒,因此它表示僅對每條記錄計算一次表達式,而不是兩次。

+0

進一步的'證明'是當你將(1)的睡覺改變爲(2)時。現在需要9秒。 –

+0

'select x + sleep(1),count from(t)by x + sleep(1)'需要6秒。怎麼了?也許最初的例子轉換爲'從t'選擇不同的x + sleep(1)。 –

+0

此外:'選擇x +睡眠(1)作爲c從t有c> 0'也需要6秒鐘。所以我猜,每個別名都會被內部替換爲它後面的表達式,並且每次都會進行評估。 –

1

在諮詢了一位MySQL工程師之後,我提出了這個冗長的答案。

  • 緩存 - 沒有查詢的任何部分被'記住'以便以後在該(或後續)查詢中使用。 (對比:查詢緩存。)
  • 常見子表達式消除 - no。這是一種常用的編譯器技術,但MySQL不使用它。例如:(a-b)*(a-b)將執行兩次相減。
  • 從循環中刪除一個常量 - 是的,有限制。這是另一種編譯器技術。
  • 各種以SQL爲中心的黑客 - 是的;見下文。
  • 重新評估子查詢 - 它取決於。此外,優化器正在逐漸變得更好。
  • VIEWs - 這取決於。仍然有一些情況下,VIEW註定要比等效的SELECT差。例如:在VIEW中沒有條件下推UNION。其實,這更多是延誤行動的問題。

(警告:我沒有在任何我的回答100%的信心,但我相信大部分是正確的,像MySQL 5.7,MariaDB的10.1等)的

思考多排SELECT作爲一個循環。很多,也許是所有的「確定性」表達式都會被評估一次。示例:常量日期表達式,甚至涉及函數調用。但是...

NOW()在查詢開始時會被特別評估一次。此外,該值在複製時傳遞給從站。也就是說,查詢存儲在從站時,NOW()可能已過時。(SYSDATE()是另一種動物。)

特別是隨着only_full_group_by來臨,GROUP BY需要知道它的SELECT表達式匹配。所以,這看起來類似的代碼。

HAVINGORDER BY可以使用來自SELECT列表別名(不像WHEREGROUP BY)。因此SELECT expr AS x ... HAVING expr似乎重新評估expr,但SELECT expr AS x ... HAVING x似乎達到了已評估的expr

MariaDB 10.2的窗口功能對它們可以/不能重用的位置有一些相當嚴格的限制;我還沒有完整的照片。

一般來說,這些都不重要 - 表達式的重新評估(DATE(date)或甚至COUNT(*))將得到相同的答案。此外,通過行查找通常比表達式評估花費更多。所以,除非你有一個好的秒錶,否則你不會說出差異。

+0

考慮到其他答案中的演示,這是否意味着它認爲「SLEEP(1)」是一個確定性表達式,因此它只評估一次? – Barmar

+0

確定性 - 沒有。否則查詢將花費1秒,而不是3.我認爲'x + sleep(1)'屬於我在'GROUP BY'上發言的黃鼠狼字詞。請注意,GROUP BY x + sleep(2)'需要9秒;我不知道在'only_full_group_by'檢查中是否涉及'x'。 –