2011-04-22 62 views
10

我試圖優化這個查詢的限制和秩序:MySQL查詢:文件排序時內部聯接,通過

SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles_authors.fk_Authors=586 
ORDER BY articles.publicationDate LIMIT 0,50; 

表的文章:

  • 引擎:MyISAM數據
  • ROW_FORMAT :動態
  • 行數:1 482 588
  • 數據長度:788 926 672
  • 最大數據長度:281 474 976 710 655
  • 索引長度:127 300 608
  • 數據免費:0
  • 校驗:空
 CREATE TABLE `articles` (
     `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
    `title` VARCHAR(255) NOT NULL, 
    `publicationDate` DATE NOT NULL DEFAULT '1970-01-01', 
    PRIMARY KEY (`id`), 
    KEY `publicationDate` (`publicationDate`) 
    ) ENGINE=MYISAM AUTO_INCREMENT=1498496 DEFAULT CHARSET=utf8

表articles_authors:

  • 引擎:MyISAM
  • ROW_FORMAT:動態
  • 行數:1 970 750
  • DATA_LENGTH:45 008 420
  • 最大數據長度:281 474 976 710 655
  • 索引長度:127 300 608
  • 數據免費:0
  • 校驗:空
 CREATE TABLE `articles_authors` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `fk_Articles` int(10) unsigned NOT NULL, 
    `fk_Authors` int(10) unsigned NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `fk_Articles_fk_Authors` (`fk_Articles`,`fk_Authors`), 
    KEY `fk_Articles` (`fk_Articles`), 
    KEY `fk_Authors` (`fk_Authors`), 
    ) ENGINE=MyISAM AUTO_INCREMENT=2349047 DEFAULT CHARSET=utf8

解釋上查詢:

id (1), select_type(SIMPLE), TABLE(articles_authors), TYPE(ref), possible_keys(fk_Articles_fk_Authors, fk_Articles, fk_Authors), KEY (fk_Authors), Key_len(4), ref(const), ROWS(171568), extra (USING TEMPORARY; USING FILE sort) 
id (1), select_type(SIMPLE), TABLE(articles), TYPE(eq_ref), possible_keys(PRIMARY), KEY (PRIMARY), Key_len(4), ref(articles_authors.fk_Authors), ROWS(1), extra() 

正如你所看到的,SQL查詢不優化(使用文件排序中講解)。

感謝您的幫助!

+1

+1,對於一個有據可查的問題!當人們真正包含相關信息時就喜歡它! – 2011-04-22 17:59:09

+0

我不明白這可以如何優化更多,因爲在where/order子句中,您具有來自兩個不同表格的值,並且您無法創建組合索引'(fk_Authors,publicationDate)' – Pentium10 2011-04-22 21:30:22

+0

編輯答案包括去標準化選項。 – Johan 2011-04-26 12:31:13

回答

1

使用索引,就像它在解釋中說的那樣。

id (1), select_type(SIMPLE), TABLE(articles_authors), TYPE(ref),
possible_keys(fk_Articles_fk_Authors, fk_Articles, fk_Authors),

KEY (fk_Authors), Key_len(4), ref(const), ROWS(171568),
extra (USING TEMPORARY; USING FILE sort)

僅作爲額外爲它選擇了50行,比按出版日期順序它做一個文件排序。
它創建一個包含50個項目的臨時表。然後它與桌面排序。
這個這樣做,因爲MySQL不能在那些孤獨的50個項目上使用大索引,它會花費很多IO訪問時間。

在內存中對50個數字進行排序然後訪問磁盤上的索引會更快。

你可以做一些事情來,雖然加快了查詢:

optimize table articles, articles_authors 

,並重新運行查詢。

編輯:由非規範化表項目

加快建議如果你重寫本查詢:

SELECT articles.id FROM articles WHERE articles.id IN (
    SELECT articles_authors.fk_articles WHERE articles_authors.fk_authors = 586 
    LIMIT 0,50 
) 
ORDER BY articles.publicationDate; 

你可能會看到相同的性能,而它突出的問題。 如果作者586有180,000篇文章,那麼MySQL必須從articles_authors中的180k中搜索50項,然後在訂單表中再次從180k中搜索50項。

如果您合併表article_authors和文章,您的表文章將非規範化(假設文章可以有多個作者)但您不必進行連接,並且您自己保存第二個搜索。

CREATE TABLE `articles` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `publicationDate` date NOT NULL DEFAULT '1970-01-01', 
    `title` varchar(255) NOT NULL, 
    `fk_Authors` int(10) unsigned NOT NULL, 
PRIMARY KEY (`id`), 
UNIQUE KEY `Articles_fk_Authors` (`id`,`fk_Authors`), 
KEY `fk_Authors` (`fk_Authors`), 
KEY `publicationDate` (`publicationDate`) 
) ENGINE=MyISAM AUTO_INCREMENT=2349047 DEFAULT CHARSET=utf8 

現在,您可以從中選擇像這樣

SELECT articles.id FROM articles WHERE articles.Author = 586 
ORDER BY articles.publicationDate LIMIT 50,0 
+0

感謝您的回覆。 我執行「優化表文章articles_authors」。但性能問題仍然出現。 有一位作者有180 000篇文章。查詢需要超過30秒,並且會注意到大IO訪問。 – heisenberg 2011-04-26 08:22:53

0
SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles.id=586 
ORDER BY articles.publicationDate LIMIT 0,50; 
+0

選擇文本並按下「{}」按鈕將爲您設置SQL代碼的格式。我認爲每行開頭的四個空格將會執行相同的操作 – 2011-05-03 20:38:59

+0

@Conrad:這就是所有的'{}'所做的......將4個字符放在選定文本塊中每行的開頭。 – 2011-05-05 18:33:49

1

也許這將幫助你:

SELECT articles.id 
    FROM articles 
     INNER JOIN (SELECT fk_Articles FROM articles_authors WHERE articles_authors.fk_Authors=586) sub ON articles.id=sub.fk_Articles 
ORDER BY articles.publicationDate LIMIT 0,50; 
0

不知道,但康拉德的建議似乎改變排序和限制,因此您可能會按排序順序獲得隨機列表的前50項,而不是排序列表的前50項。

如果按照fk_author,publicationDate和索引排序,可以使用聯接幫助來查看視圖嗎?還取決於你正在優化,速度或磁盤空間?

你可以在Mysql中使用IN嗎?它可能會更好地優化嗎? (示例代碼,未選中)

SELECT id FROM articles WHERE id IN 
(SELECT fk_Articles FROM articles_authors WHERE fk_Authors=586) as IDs 
ORDER BY publicationDate LIMIT 0,50; 
0

這實際上可能是有效的,這取決於您的數據。

SELECT articles.id 
FROM articles 
INNER JOIN articles_authors ON articles.id=articles_authors.fk_Articles 
WHERE articles_authors.fk_Authors=586 
ORDER BY articles.publicationDate LIMIT 0,50; 

如果articles_authors.fk_Authors = 586所根據您的數據庫引擎收集的統計數據導致相當罕見的行,它會更便宜,以獲取所有和取前50行。

相反,如果它導致大部分文章,查閱articles.publicationDate上的索引將會更便宜,並過濾掉無效行直到獲得所需的50行。