2016-07-19 63 views
0

我正在優化我的查詢並發現了一些我無法控制的問題。子查詢處理多於必要的行數

我使用下面的查詢選擇了一堆類的,他們用別名從包含新老別名類別表結合:

SELECT `c`.`id` AS `category.id`, 
    (SELECT `alias` 
    FROM `aliases` 
    WHERE category_id = c.id 
    AND `old` = 0 
    AND `lang_id` = 1 
    ORDER BY `id` DESC 
    LIMIT 1) AS `category.alias` 
FROM (`categories` AS c) 
WHERE `c`.`status` = 1 AND `c`.`parent_id` = '11'; 

只有2個類別,有值11對於parent_id,所以它應該從別名表中查找2個類別。

enter image description here

不過,如果我用EXPLAIN它說,它必須處理48行。別名表也包含每個類別1個條目(在這種情況下,它可以更多)。一切都被索引,如果我理解正確,因此它應該立即找到正確的別名。

enter image description here

現在,這裏是奇怪的事情。當我不根據條件中的類別比較別名時,但通過查詢返回的類別ID手動比較別名時,它只處理1行,與索引一樣。

所以我通過WHERE category_id IN (37, 43)更換WHERE category_id = c.id和查詢變快:

enter image description here

我能想到的唯一的事情就是子查詢沒有結束從查詢的結果運行,但之前的一些過濾已經完成了。歡迎任何形式的解釋或幫助!

編輯:傻我,WHERE IN不起作用,因爲它不會作出獨特的選擇。問題仍然存在!

創建表模式

CREATE TABLE `aliases` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `lang_id` int(2) unsigned NOT NULL DEFAULT '1', 
    `alias` varchar(255) DEFAULT NULL, 
    `product_id` int(10) unsigned DEFAULT NULL, 
    `category_id` int(10) unsigned DEFAULT NULL, 
    `brand_id` int(10) unsigned DEFAULT NULL, 
    `page_id` int(10) unsigned DEFAULT NULL, 
    `campaign_id` int(10) unsigned DEFAULT NULL, 
    `old` tinyint(1) unsigned DEFAULT '0', 
    PRIMARY KEY (`id`), 
    KEY `product_id` (`product_id`), 
    KEY `category_id` (`category_id`), 
    KEY `page_id` (`page_id`), 
    KEY `alias_product_id` (`product_id`,`alias`), 
    KEY `alias_category_id` (`category_id`,`alias`), 
    KEY `alias_page_id` (`page_id`,`alias`), 
    KEY `alias_brand_id` (`brand_id`,`alias`), 
    KEY `alias_product_id_old` (`alias`,`product_id`,`old`), 
    KEY `alias_category_id_old` (`alias`,`category_id`,`old`), 
    KEY `alias_brand_id_old` (`alias`,`brand_id`,`old`), 
    KEY `alias_page_id_old` (`alias`,`page_id`,`old`), 
    KEY `lang_brand_old` (`lang_id`,`brand_id`,`old`), 
    KEY `id_category_id_lang_id_old` (`lang_id`,`old`,`id`,`category_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=112392 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT; 
+0

Eh?該模式與「SELECT」不匹配 - 「status」和「parent_id」在哪裏? –

+0

'status'和'parent_id'來自主表('categories'),而不是子查詢(注意'c'別名) – Tumtum

回答

1
SELECT ... 
    WHERE x=1 AND y=2 
    ORDER BY id DESC 
    LIMIT 1 

將通過以下幾種方法之一來執行。

既然你已經不是我們顯示你有指標(SHOW CREATE TABLE),我將介紹一些可能的情況下...

  • INDEX(x, y, id) - 這可以找到該條件的最後一排,所以也沒有需要看多行。
  • 某些其他索引或無索引:從最後的id掃描DESCending,檢查每行x=1 AND y=2,當發現(如果)這樣的行時停止。
  • 某些其他索引或無索引:掃描整個表格,檢查每行的x=1 AND y=2;將它們收集到臨時表中;按id排序;交付一行。

一些EXPLAIN線索:

  • 使用Where - 沒有說太多
  • 使用文件排序 - 它做了一個排序,顯然爲ORDER BY。 (它可能已完全在RAM中完成;忽略'文件')
  • 使用索引條件(而不是「使用索引」) - 這表示內部優化,它可以比以前更有效地檢查WHERE子句在舊版本中。

不要在EXPLAIN信任的 「行」。通常他們是合理的正確的,但有時他們被關閉了數量級。這裏是一個更好的方式請參見「多少工作」是在一個相當快速查詢正在做:

FLUSH STATUS; 
SELECT ...; 
SHOW SESSION STATUS LIKE 'Handler%'; 

隨着CREATE TABLE,我可以對如何提高指標的建議。

+0

感謝您的補充信息。很有意思!雖然我很難理解會話狀態查詢的結果。 – Tumtum

+0

我添加了CREATE模式。奇怪的是,在我的本地機器上,它確實使用基於密鑰的更好的索引。 – Tumtum

+0

簡單解釋'STATUS':「一些大數字=慢;所有小數字=快」 –