2010-11-19 91 views
0

MySQL文檔在section 11.5.3中聲明,儘管SQL標準可能會說,但使用列在SELECT子句中不在GROUP BY子句中,只要它們在功能上依賴於分組鍵。MySQL聲稱我可以在SELECT中使用不在GROUP BY中的列,但我不能以相同的性能

MySQL的擴展使用GROUP BY的這樣 可以使用非聚合列 或計算在選擇列表 沒有出現在GROUP BY子句 。您可以使用此功能 通過避免 不必要的列排序和 分組獲得更好的性能。例如,你不需要下面的查詢在 上customer.name 組:

SELECT order.custid, customer.name, 
MAX(payments) FROM order,customer 
WHERE order.custid = customer.custid 
GROUP BY order.custid; 

在標準 SQL,你將不得不 customer.name添加到GROUP BY子句。 在MySQL中,名稱是多餘的。

聽起來很合理。但是,雖然我可以選擇這些列,但它似乎對性能有不利影響。

EXPLAIN SELECT o.id FROM objects o GROUP BY o.id; 
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra     | 
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ 
| 1 | SIMPLE  | o  | range | NULL   | PRIMARY | 3  | NULL | 5262 | Using index for group-by | 
+----+-------------+-------+-------+---------------+---------+---------+------+------+--------------------------+ 

(我意識到,這個查詢是非常愚蠢的。它只是一個更復雜的查詢具有相同問題的最簡單的版本)當只選擇主鍵ID I組,那麼MySQL使用主關鍵指標。但是,當我包含其他列時,MySQL不會。

EXPLAIN SELECT o.id, o.name FROM objects o GROUP BY o.id; 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra   | 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 
| 1 | SIMPLE  | o  | ALL | NULL   | NULL | NULL | NULL | 5261 | Using filesort | 
+----+-------------+-------+------+---------------+------+---------+------+------+----------------+ 

使用filesort而不是索引真的讓我回想起來。我目前希望從該表中選擇*,所以希望避免重複組中的所有列並將它們編入索引。有沒有什麼辦法讓MySQL使用主鍵索引,就像我期望的那樣?

回答

0

選擇由於這看起來並不像有一個簡單的答案什麼表,我與一時還便宜的解決方案去。

做的是什麼東西像下面這樣:

SELECT o1.* FROM objects o1 WHERE o1.id IN (SELECT o2.id FROM objects o2 WHERE mycondition GROUP BY o2.id) 

然而,根據它如何得到EXPLAIN版,MySQL優化觀看子查詢爲依賴,這始終是一個真正的,真正令人討厭的表現殺手。我認爲這是查詢優化器帶來的一個錯誤,即它是同一個表,即使它是別名。因此,我將使用一個查詢來獲取這些ID,並將其作爲IN作爲提取o.*的第二個查詢。它得到合理的表現,並且不是痛苦。

這個問題仍然是開放與如果沒有更好:)

0

使用派生表組通過,並加入回要從

+0

我可以在技術上使用子查詢和派生表,但目前形式的MySQL優化器(至少在Debian存儲庫的最新版本中)將它們視爲從屬子查詢,而不是提前運行,然後運行反對。我敢打賭,這是因爲它是具有不同別名的同一張桌子。 – Matchu 2010-11-19 19:29:16

0

在第一個查詢,您所訪問的唯一字段是在索引中執行爲好,更清潔的解決方案的答案,因此MySQL只需看看索引文件。但在第二個查詢中,您現在正在從表中拉出一列,這也需要讀取表數據。如果您有WHERE子句,第一個查詢並不真正使用您的主鍵索引。它僅用於組,但它仍在查看索引中的每個條目。

第一個查詢和第二個查詢之間的區別僅在於第二個查看整個表(又名錶掃描)中的每一行,而不是索引中的每個主鍵值。

至於優化去,如果你真正的查詢有沒有累積函數(SUM,COUNT等),如您的示例然後應該看到一個重大的改進只是在做:

SELECT DISTINCT o.id, o.name FROM objects o 

但是,如果這是唯一的對於你的簡單例子,你的查詢確實需要GROUP BY,那麼你的下一個最好的選擇就是增加tmp_table_size and max_heap_table_size變量,以允許更多的行一次裝入內存。

相關問題