2011-04-18 29 views
1

如何讓MySQL使用具有下表結構和查詢的鍵/索引?讓MySQL使用一個索引/鍵值,其中1列和2列依次爲

-- the table 
CREATE TABLE `country` (
    `id` int(11) NOT NULL auto_increment, 
    `expiry_date` datetime NOT NULL, 
    `name` varchar(50) collate utf8_unicode_ci NOT NULL, 
    `symbol` varchar(5) collate utf8_unicode_ci NOT NULL, 
    `exchange_rate` decimal(11,5) NOT NULL default '1.00000', 
    `code` char(3) collate utf8_unicode_ci NOT NULL, 
    `currency_code` varchar(3) collate utf8_unicode_ci NOT NULL, 
    `display_order` smallint(6) unsigned NOT NULL default '0', 
    PRIMARY KEY (`id`), 
    KEY `code` (`code`), 
    KEY `currency_code` (`currency_code`), 
    KEY `display_order` (`expiry_date`,`name`,`display_order`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

-- the query 
SELECT `country`.* 
FROM `country` 
WHERE `country`.`expiry_date` = 0 
ORDER BY `country`.`display_order` ASC, `country`.`name` ASC; 

我想要得到它,因爲結果與180的查詢時間0.0013s,是迄今爲止頁面(3倍於下一個慢長)最慢的查詢使用的密鑰。根據我的理解,查詢應該使用display_order索引/鍵。

+0

沒有,你應該在display_order創建一個單獨的指標! – 2011-04-18 20:47:20

+0

和一個單獨的「名字」 – 2011-04-18 20:47:58

+0

對不起,不確定你的意思是什麼Q.C.我應該爲每個領域有單獨的索引嗎? – 2011-04-18 20:49:04

回答

2

將其更改爲:

CREATE TABLE `country` (
    `id` int(11) NOT NULL auto_increment, 
    `expiry_date` datetime NOT NULL, 
    `name` varchar(50) collate utf8_unicode_ci NOT NULL, 
    `symbol` varchar(5) collate utf8_unicode_ci NOT NULL, 
    `exchange_rate` decimal(11,5) NOT NULL default '1.00000', 
    `code` char(3) collate utf8_unicode_ci NOT NULL, 
    `currency_code` varchar(3) collate utf8_unicode_ci NOT NULL, 
    `display_order` smallint(6) unsigned NOT NULL default '0', 
    PRIMARY KEY (`id`), 
    KEY `code` (`code`), 
    KEY `currency_code` (`currency_code`), 
    KEY `expiry` (`expiry_date`,`name`,`display_order`) <<- renamed key for clarity 
    /* always name compound keys for their left-most parts*/ 
    KEY `name` (`name`) <<-- new key here 
    KEY `display` (`display_order`) <<--new key here 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

-- the query 
SELECT `country`.* 
FROM `country` 
WHERE `country`.`expiry_date` = 0 
ORDER BY `country`.`display_order` ASC, `country`.`name` ASC; 

複合索引是棘手
MySQL並沒有使用複合索引中的name索引,因爲name位於中間,並且MySQL僅使用索引的某些部分,如果該部分是最左邊的部分的一個複合索引。
字段顯示順序的索引也是如此。其中包含display_order的複合索引使用該字段,因爲它是最右邊的部分,因此不會進行排序。

解決方案
讓現場name
和現場display_order一個單獨的索引單獨的索引。

有時MySQL不使用索引,即使有索引可用。出現這種情況的一種情況是,優化程序估計使用索引需要MySQL訪問表中非常大部分的行。 (在這種情況下,表掃描可能會更快,因爲它需要更少的搜索。)但是,如果這樣的查詢使用LIMIT來只檢索一些行,MySQL無論如何都會使用索引,因爲它可以更快地找到結果中返回的幾行。

此外,如果行的大百分比具有相同的值的字段(> 40%(這個))然後MySQL不會使用索引。

參見:http://dev.mysql.com/doc/refman/5.1/en/mysql-indexes.html

參見:http://dev.mysql.com/doc/refman/5.1/en/index-hints.html
關於如何迫使指數按FractalizeR建議。
請務必及時你選擇迫使指數
在這樣一個簡單的查詢後,MySQL的似乎不太可能是錯的,你的0.0013秒之間選擇的時間表明,有幾個在表中。
由於上述規定的百分比規則,索引無法像表格中行數很少時所預期的那樣工作。

請注意,在這種情況下,強制索引不起作用,因爲您無法強制MySQL使用複合索引的最右側部分。它不能那樣做。

+0

所以我認爲問題是大多數(238 239)都有一個display_order爲100,但我改變這些,他們增量,因此匹配名稱順序和查詢採取完全相同的時間,仍然不使用鍵。我也嘗試了一個名字的關鍵,但也沒有區別。任何其他想法? – 2011-04-19 06:47:43

+0

@Darryl請參閱我上面所做的編輯,字段display_order是複合索引的最右邊部分,並且不起作用,錯過了,因爲錯誤非常微妙。 – Johan 2011-04-19 07:47:14

+0

我有一個名稱,display_order和expiry_date單獨的索引,但仍然沒有使用。 – 2011-04-19 07:56:43

1

您的查詢在{display_order} + {name}列上有ORDER BY,而 名爲「display_order」的索引實際上已在列{expiry_date} + {name} + {display_order}上定義。

索引中列的順序很重要。如果您需要對作爲索引開頭的列進行篩選排序,則可以使索引受益。 如果您記住索引是預先排序的信息,這就變得很明顯。

如果您希望獲得{display_order} + {name}上的索引,那麼您需要一個以{display_order} + {name}開頭的索引。例如{display_order} + {name}或{display_order} + {name} + {expiry_date}。

因此,爲了優化您的查詢,您必須更改表中的索引或查詢中的SORT子句。

1

你可以做的最後一件事是,使用「FORCE INDEX」由fractalizeR

作爲mentionten
相關問題