2014-03-13 30 views
3

我有兩個表,應用程序和pricehistory 有一個主要索引id在應用程序上是一個int 在pricehistory我有兩個字段id_app(int),price(float )和dateup(日期)和一個唯一索引「id_app,dateup」Mysql使用filesort即使當使用索引和只有一行

我試圖讓一個應用程序的最新版本(日)價格:

select app.id, 
     ( select price 
      from pricehistory 
      where id_app=app.id 
      order by dateup desc limit 1) 
from app 
where id=147 

的解釋選擇是怎麼樣的奇怪,因爲它返回1行,但它仍然使一個文件夾:

id select_type  table  type possible_keys   key  key_len ref  rows Extra  
1 PRIMARY   app   const PRIMARY     PRIMARY  4  const  1 
2 DEPENDENT SUBQUERY pricehistory ref id_app,id_app_2,id_app_3 id_app  4  const  1 Using where; Using filesort 

爲什麼只有1行時需要filesort?爲什麼它的文件排序時我索引所有的IT需求(id_app和dateup)

應用程序有一個百萬行,我使用的是InnoDB

編輯:一個SQL小提琴說明問題:

http://sqlfiddle.com/#!2/085027/1

EDIT3: 新撥弄着同樣的問題另一個請求: http://sqlfiddle.com/#!2/f7682/6

edit4:這個小提琴(http://sqlfiddle.com/#!2/2785c/2)顯示建議的查詢不起作用,因爲它選擇了所有來自pricehistory的數據,只是爲了獲取我想要的數據。

+0

什麼是你的MySQL版本?我已經在版本5.7上試過這個查詢,當「id_app,dateup」上的組合索引已經創建時,它沒有使用filesort。 – krokodilko

+0

5.5.35-0 + wheezy1-log(Debian) – user3416510

+0

我已經添加了一個帶有2個索引的sql小提琴,而mysql使用的是最長的索引而不是最短的,但我不知道爲什麼 – user3416510

回答

1

這裏的拇指快速規則,以命令列應該在索引中去:

  1. 列有平等的條件(=)在WHERE子句中引用。
  2. 選擇一個的:

    一個。 ORDER BY子句中引用的列。

    b。 GROUP BY子句中引用的列。

    c。帶有範圍條件的WHERE子句中引用的列(!=,>,<,IN,BETWEEN,IS [NOT] NULL)。

  3. 在SELECT列表中引用的列。

How to Design Indexes, Really.

在這種情況下,我能夠用這個索引中刪除的文件排序:

mysql> alter table pricehistory add key bk1 (id_app, dateup, price_fr); 

而這裏的講解,沒有顯示出文件排序,並改善「使用索引「:

mysql> explain select price_fr from pricehistory where id_app=1 order by dateup desc\G 
*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: pricehistory 
     type: ref 
possible_keys: bk1 
      key: bk1 
     key_len: 4 
      ref: const 
     rows: 1 
     Extra: Using where; Using index 

如果需要,可以使此索引爲UNIQUE。

我不得不放棄其他唯一鍵,以避免混淆優化器。

+0

當我使用查詢作爲子查詢時,它仍然做一個文件夾,但不是如果我直接請求 – user3416510

+0

@ user3416510,謝謝你的報告。是的,在某些情況下,子查詢優化得不好,索引也無濟於事。您最好測試兩種查詢形式,並比較性能和優化。這是最佳做法。 –

0

這兩個UNIQUE KEY正在引發該問題。我改變了你的小提琴以下,和它的作品沒有一個文件排序:

CREATE TABLE IF NOT EXISTS `pricehistory` (
    `id_app` int(10) unsigned NOT NULL, 
    `dateup` date NOT NULL, 
    `price_fr` float NOT NULL DEFAULT '-1', 
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `id_app` (`id_app`,`dateup`,`price_fr`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=345 ; 


INSERT INTO pricehistory 
(id_app, price_fr,dateup) 
VALUES 
('1', '4.99', now()), 
('2', '0.45', now()); 

EXPLAIN給出:

ID SELECT_TYPE TABLE   TYPE POSSIBLE_KEYS KEY   KEY_LEN REF ROWS EXTRA 
1  SIMPLE   pricehistory ref  id_app   id_app  4   const 1  Using where; Using index 

沒有理由使用兩個(id_app,dateup)(id_app,price_fr,dateup)一個UNIQUE KEY,因爲它們是多餘的。我非常有信心,冗餘正在使MySQL對本身不確定,所以它在做一個文件系統時會發生錯誤。

+0

唯一的關鍵字不像PRIMARY,你可以有多個唯一索引。我試圖修改第二個索引而不是刪除到: – user3416510

+0

我意識到這一點。關鍵是你的原始代碼中的'UNIQUE KEY'是多餘的 - 其中一個包含了另一箇中使用的所有列。無論如何,至少在小提琴中,重組如上所述以消除冗餘可以阻止MySQL使用filesort。此外,僅供參考,您的代碼沒有通過您的評論。 –

+0

唯一關鍵字不像PRIMARY,您可以擁有多個唯一索引。我嘗試修改第二個索引,而不是刪除到: UNIQUE KEY'id_app'('id_app','price_fr','dateup'), UNIQUE KEY' id_app_3'('id_app','dateup') and eventough它們不一樣,它仍然使用filesort。但我會刪除「唯一」的索引之一,謝謝 – user3416510

0

解決方法是刪除唯一的索引之一。看起來如果它沒有用,最好不要使用唯一關鍵字。 感謝你們倆

編輯: 該死的,用2臺不同的查詢,該文件排序又回來了: http://sqlfiddle.com/#!2/f7682/6

+0

我會很想改變例如,從應用 到'解釋 選擇app.id,MAX(pricehistory.dateup) LEFT OUTER JOIN pricehistory ON pricehistory.id_app = app.id 和price_fr!= -1 哪裏app.id> 0;'這消除了相關的子查詢以及filesort。 – Kickstart

+0

我不能這樣做,因爲「按dateup desc limit 1排序」在您的查詢中消失了。我需要最新的pricehistory和只有一個(可以有多個價格相同的id_app) – user3416510

+0

它仍然可以刪除依賴子查詢,但如果它不只是最新的dateup你想要的,那麼你將需要讓我們知道您需要哪些列(您可以針對非依賴子查詢執行連接以獲取每個示例的最大日期,然後再次與pricehistory結合以獲取該id_app和dateup的其餘行) – Kickstart

相關問題