嵌套集索引问题

时间:2015-12-10 12:42:15

标签: mysql

我的查询运行有点慢。它需要一个包含搜索结果的项目的表格,然后获取包含这些项目中的一个或多个的类别。类别(~300)存储在具有大约4个级别的嵌套集模型中。我需要知道每个级别的计数(因此一个项目可能属于大孩子类别,但也需要计入子级和父级类别)。

执行此操作的查询如下: -

SELECT  category_parent.id, 
        category_parent.depth, 
        category_parent.name AS item_sub_category, 
        category_parent.left_index, 
        category_parent.right_index, 
        COUNT(DISTINCT item.id) as total
FROM search_enquiries_found 
        INNER JOIN item ON search_enquiries_found.item_id = item.id 
        INNER JOIN category ON item.mmg_code = category.mmg_code
        INNER JOIN category category_parent ON category.left_index BETWEEN category_parent.left_index AND category_parent.right_index
WHERE search_enquiries_found.search_enquiry_id = 35 
AND item.cost_price > 0 
GROUP BY category_parent.id, 
        category_parent.depth, 
        item_sub_category, 
        category_parent.left_index, 
        category_parent.right_index
ORDER BY category_parent.left_index

发现大约12,000条记录,这需要大约1.5秒。解释如下: -

id  select_type table                   partitions  type    possible_keys                                                               key                 key_len     ref                             rows    Extra
1   SIMPLE      category                NULL        ALL     left_index                                                                  NULL                NULL        NULL                            337     Using temporary; Using filesort
1   SIMPLE      item                    NULL        ref     PRIMARY,id,mmg_code                                                         mmg_code            27          em_entaonline.category.mmg_code 43      Using where
1   SIMPLE      search_enquiries_found  NULL        eq_ref  search_enquiry_id,item_id,search_enquiry_id_2,search_enquiry_id_relevance   search_enquiry_id   8           const,em_entaonline.item.id     1       Using index
1   SIMPLE      category_parent         NULL        ALL     left_index                                                                  NULL                NULL        NULL                            337     Range checked for each record (index map: 0x2)

重要的表是分层类别表: -

CREATE TABLE `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(128) NOT NULL,
  `depth` int(11) NOT NULL,
  `is_active` tinyint(1) NOT NULL DEFAULT '1',
  `left_index` int(4) NOT NULL,
  `right_index` int(4) NOT NULL,
  `mmg_code` varchar(25) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `left_index` (`left_index`,`right_index`),
  UNIQUE KEY `depth` (`depth`,`left_index`,`right_index`),
  KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

使用连接上的任何索引来获取父类别是没有用的。

更改此查询以删除大多数分组字段并仅使用唯一ID(实际上组中的其他字段不是必需的,但我倾向于将它们包括在内以保持严格的标准)减少所需的时间降至~0.5秒: -

SELECT  category_parent.id, 
        category_parent.depth, 
        category_parent.name AS item_sub_category, 
        category_parent.left_index, 
        category_parent.right_index, 
        COUNT(DISTINCT item.id) as total
FROM search_enquiries_found 
        INNER JOIN item ON search_enquiries_found.item_id = item.id 
        INNER JOIN category ON item.mmg_code = category.mmg_code
        INNER JOIN category category_parent ON category.left_index BETWEEN category_parent.left_index AND category_parent.right_index
WHERE search_enquiries_found.search_enquiry_id = 35 
AND item.cost_price > 0 
GROUP BY category_parent.id
ORDER BY category_parent.left_index

对此的解释略有不同: -

id  select_type table                   partitions  type    possible_keys                                                               key                 key_len     ref                             rows    Extra?
1   SIMPLE      category                NULL        ALL     left_index                                                                  NULL                NULL        NULL                            337     Using temporary; Using filesort
1   SIMPLE      item                    NULL        ref     PRIMARY,id,mmg_code                                                         mmg_code            27          em_entaonline.category.mmg_code 43      Using where
1   SIMPLE      search_enquiries_found  NULL        eq_ref  search_enquiry_id,item_id,search_enquiry_id_2,search_enquiry_id_relevance   search_enquiry_id   8           const,em_entaonline.item.id     1       Using index
1   SIMPLE      category_parent         NULL        ALL     PRIMARY,left_index,depth,name                                               NULL                NULL        NULL                            337     Using where; Using join buffer (Block Nested Loop)

现在我有两个问题。

1 - 为什么从GROUP BY中删除字段(完全依赖于组中剩余的主键)以这种方式更改EXPLAIN,性能发生如此大的变化。

2 - 在任何一种情况下,性能都很差,没有用于连接到父类别表的密钥。有关改进的建议吗?

另外一点,我尝试使用STRAIGHT_JOIN来反转连接的顺序并强制它。在这种情况下,这进一步改善了性能(~0.2秒),但这是一种不寻常的情况(因为它从父类别表中返回所有记录 - 大多数时候搜索将返回更少的记录,因此远远少于类别),所以我认为这会在更正常的情况下减慢速度,并且仍然没有在某些连接上使用索引。如果MySQL根据结果集决定加入表的方式会很好。

SELECT  category_parent.id, 
        category_parent.depth, 
        category_parent.name AS item_sub_category, 
        category_parent.left_index, 
        category_parent.right_index, 
        COUNT(DISTINCT item.id) as total
FROM category category_parent
STRAIGHT_JOIN category ON category.left_index BETWEEN category_parent.left_index AND category_parent.right_index
STRAIGHT_JOIN item ON item.mmg_code = category.mmg_code
STRAIGHT_JOIN search_enquiries_found ON search_enquiries_found.item_id = item.id 
WHERE search_enquiries_found.search_enquiry_id = 35 
GROUP BY category_parent.id, 
        category_parent.depth, 
        item_sub_category, 
        category_parent.left_index, 
        category_parent.right_index
ORDER BY category_parent.left_index

id  select_type table                   partitions  type    possible_keys                                                               key                 key_len     ref                             rows    Extra 
1   SIMPLE      category_parent         NULL        ALL     left_index                                                                  NULL                NULL        NULL                            337     Using temporary; Using filesort
1   SIMPLE      category                NULL        ALL     left_index                                                                  NULL                NULL        NULL                            337     Range checked for each record (index map: 0x2)
1   SIMPLE      item                    NULL        ref     PRIMARY,id,mmg_code                                                         mmg_code            27          em_entaonline.category.mmg_code 43      Using index
1   SIMPLE      search_enquiries_found  NULL        eq_ref  search_enquiry_id,item_id,search_enquiry_id_2,search_enquiry_id_relevance   search_enquiry_id   8           const,em_entaonline.item.id     1       Using index

0 个答案:

没有答案