使用许多左连接优化简单查询

时间:2017-10-10 13:23:59

标签: mysql optimization query-optimization

我有一个很大的查询,用于搜索' map_item'。

SELECT map_item_name
FROM map_item
LEFT JOIN map_section_item ON map_section_item_item_id = map_item_id
LEFT JOIN map_section ON map_section_id = map_section_item_section_id
LEFT JOIN map_item_flag ON map_item_flag_item_id = map_item_id
LEFT JOIN flag ON flag_id = map_item_flag_flag_id
LEFT JOIN map ON map_id = map_section_map_id
LEFT JOIN place_map ON place_map_map_id = map_id
LEFT JOIN place ON place_id = place_map_place_id
LEFT JOIN place_category ON place_category_place_id = place_id
LEFT JOIN category ON category_id = place_category_category_id
LEFT JOIN review ON review_map_item_id = map_item_id
LEFT JOIN map_price ON map_price_item_id = map_item_id
LEFT JOIN county_list ON place_address_county = county_id

' map_item'共有5399条记录,并且所有连接表中都没有大量数据。

如果我在没有左连接(SELECT map_item_name FROM map_item)的情况下运行此查询,则按预期返回0.00s,但上面的连接查询大约需要10.00s。

由于用户可以应用于搜索的不同过滤器,查询中需要所有左连接,但原始查询需要很长时间才能运行(大约20秒),并且在删除大多数之后查询的一部分我留在上面(这只是左连接),甚至这需要18秒才能运行。

以下是查询中的解释声明:

+----+-------------+-------------------+--------+----------------------------------+----------------------------------+---------+-----------------------------------------------------------+------+-----------------------------------------------------------------+
| id | select_type | table             | type   | possible_keys                    | key                              | key_len | ref                                                       | rows | Extra                                                           |
+----+-------------+-------------------+--------+----------------------------------+----------------------------------+---------+-----------------------------------------------------------+------+-----------------------------------------------------------------+
|  1 | SIMPLE      | map_item          | ALL    | NULL                             | NULL                             | NULL    | NULL                                                      | 5455 | NULL                                                            |
|  1 | SIMPLE      | map_section_item  | index  | NULL                             | map_section_item_section_id      | 8       | NULL                                                      | 5330 | Using where; Using index; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | map_section       | eq_ref | PRIMARY                          | PRIMARY                          | 4       | bestmeal.map_section_item.map_section_item_section_id     |    1 | NULL                                                            |
|  1 | SIMPLE      | map_item_flag     | ALL    | NULL                             | NULL                             | NULL    | NULL                                                      | 1509 | Using where; Using join buffer (Block Nested Loop)              |
|  1 | SIMPLE      | flag              | eq_ref | PRIMARY                          | PRIMARY                          | 4       | bestmeal.map_item_flag.map_item_flag_flag_id              |    1 | Using index                                                     |
|  1 | SIMPLE      | map               | eq_ref | PRIMARY                          | PRIMARY                          | 4       | bestmeal.map_section.map_section_map_id                   |    1 | Using index                                                     |
|  1 | SIMPLE      | place_map         | index  | NULL                             | branch_map_branch_id             | 8       | NULL                                                      | 1275 | Using where; Using index; Using join buffer (Block Nested Loop) |
|  1 | SIMPLE      | place             | eq_ref | PRIMARY                          | PRIMARY                          | 4       | bestmeal.place_map.place_map_place_id                     |    1 | NULL                                                            |
|  1 | SIMPLE      | place_category    | ref    | place_category_place_id          | place_category_place_id          | 4       | bestmeal.place.place_id                                   |    1 | Using index                                                     |
|  1 | SIMPLE      | category          | eq_ref | PRIMARY                          | PRIMARY                          | 4       | bestmeal.place_category.place_category_category_id        |    1 | Using index                                                     |
|  1 | SIMPLE      | review            | ref    | review_map_item_id               | review_map_item_id               | 4       | bestmeal.map_item.map_item_id                             |    1 | Using index                                                     |
|  1 | SIMPLE      | map_price         | ref    | map_price_item_id                | map_price_item_id                | 4       | bestmeal.map_item.map_item_id                             |    1 | Using index                                                     |
|  1 | SIMPLE      | county_list       | eq_ref | PRIMARY                          | PRIMARY                          | 4       | bestmeal.place.place_address_county                       |    1 | Using index                                                     |
+----+-------------+-------------------+--------+----------------------------------+----------------------------------+---------+-----------------------------------------------------------+------+-----------------------------------------------------------------+

所有这些连接都是针对索引字段进行的,并且所有连接的表都没有任何不必要的索引,可以使用这些索引代替预期的索引。

在优化查询方面,我不是专家,但我努力找出我能做些什么来加快查询速度,同时保持左连接。我也无法想到任何可以在不使用连接的情况下返回相同结果的替代解决方案。

是否有人有任何想法可以帮助我提高此查询的效果或使用更快,更快的方法完成用户搜索?

修改 表格结构按要求:

CREATE TABLE `map_item` (
  `map_item_id` int(11) NOT NULL AUTO_INCREMENT,
  `map_item_account_id` int(11) NOT NULL DEFAULT '0',
  `map_item_category_id` int(11) NOT NULL,
  `map_item_name` varchar(255) DEFAULT NULL,
  `map_item_description` text,
  `map_item_tags` varchar(255) DEFAULT NULL,
  `map_item_type` set('d','f') DEFAULT NULL,
  PRIMARY KEY (`map_item_id`),
  KEY `map_item_account_id` (`map_item_account_id`),
  KEY `map_item_tags` (`map_item_tags`),
  KEY `map_item_category_id` (`map_item_category_id`),
  FULLTEXT KEY `map_item_keyword_search` (`map_item_name`,`map_item_description`,`map_item_tags`),
  FULLTEXT KEY `map_item_name` (`map_item_name`),
  FULLTEXT KEY `map_item_description` (`map_item_description`),
  FULLTEXT KEY `map_item_tags_2` (`map_item_tags`)
) ENGINE=InnoDB AUTO_INCREMENT=5420 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT 

CREATE TABLE `map_section_item` (
  `map_section_item_id` int(11) NOT NULL AUTO_INCREMENT,
  `map_section_item_section_id` int(11) NOT NULL DEFAULT '0',
  `map_section_item_item_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`map_section_item_id`),
  KEY `map_section_item_section_id` (`map_section_item_section_id`,`map_section_item_item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=24410 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `map_section` (
  `map_section_id` int(11) NOT NULL AUTO_INCREMENT,
  `map_section_map_id` int(11) NOT NULL DEFAULT '0',
  `map_section_map_draft_id` int(11) NOT NULL DEFAULT '0',
  `map_section_column` tinyint(1) NOT NULL DEFAULT '1',
  `map_section_name` varchar(255) DEFAULT NULL,
  `map_section_description` text,
  PRIMARY KEY (`map_section_id`),
  KEY `map_section_map_draft_id` (`map_section_map_draft_id`),
  KEY `map_section_map_id` (`map_section_map_id`),
  FULLTEXT KEY `index_name` (`map_section_name`)
) ENGINE=InnoDB AUTO_INCREMENT=4254 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `map_item_flag` (
  `map_item_flag_id` int(11) NOT NULL AUTO_INCREMENT,
  `map_item_flag_item_id` int(11) NOT NULL DEFAULT '0',
  `map_item_flag_flag_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`map_item_flag_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1547 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `flag` (
  `flag_id` int(11) NOT NULL AUTO_INCREMENT,
  `flag_category_id` int(11) NOT NULL DEFAULT '0',
  `flag_name` varchar(255) DEFAULT NULL,
  `flag_description` varchar(255) DEFAULT NULL,
  `flag_img` varchar(255) DEFAULT NULL,
  `flag_order` tinyint(2) NOT NULL DEFAULT '0',
  PRIMARY KEY (`flag_id`),
  KEY `flag_category_id` (`flag_category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `map` (
  `map_id` int(11) NOT NULL AUTO_INCREMENT,
  `map_account_id` int(11) NOT NULL DEFAULT '0',
  `map_name` varchar(255) DEFAULT NULL,
  `map_description` text,
  `map_type` set('d','f') DEFAULT NULL,
  `map_layout` set('columns','tabs','collapsed') DEFAULT NULL,
  PRIMARY KEY (`map_id`),
  KEY `map_account_id` (`map_account_id`)
) ENGINE=InnoDB AUTO_INCREMENT=138 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `place_map` (
  `place_map_id` int(11) NOT NULL AUTO_INCREMENT,
  `place_map_place_id` int(11) NOT NULL DEFAULT '0',
  `place_map_map_id` int(11) NOT NULL DEFAULT '0',
  `place_map_active` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`place_map_id`),
  KEY `branch_map_branch_id` (`place_map_place_id`,`place_map_map_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2176 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `place` (
  `place_id` int(11) NOT NULL AUTO_INCREMENT,
  `place_account_id` int(11) NOT NULL DEFAULT '0',
  `place_name` varchar(120) DEFAULT NULL,
  `place_alias` varchar(255) DEFAULT NULL,
  `place_description` text,
  `place_address_line_one` varchar(100) DEFAULT NULL,
  `place_address_line_two` varchar(100) DEFAULT NULL,
  `place_address_line_three` varchar(100) DEFAULT NULL,
  `place_address_town` varchar(100) DEFAULT NULL,
  `place_address_county` int(11) NOT NULL DEFAULT '0',
  `place_address_postcode` varchar(10) DEFAULT NULL,
  `place_address_latitude` decimal(11,8) DEFAULT NULL,
  `place_address_longitude` decimal(11,8) DEFAULT NULL,
  `place_phone` varchar(20) DEFAULT NULL,
  `place_email` varchar(255) DEFAULT NULL,
  `place_website` varchar(120) DEFAULT NULL,
  `place_flag_initial_email` tinyint(1) NOT NULL DEFAULT '0',
  `place_audit_admin_id` int(11) NOT NULL DEFAULT '0',
  `place_last_audit_datetime` datetime DEFAULT NULL,
  `place_created_by_admin_id` int(11) NOT NULL DEFAULT '0',
  `place_created` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `place_tried_google` int(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`place_id`),
  KEY `place_account_id` (`place_account_id`),
  KEY `place_address_county` (`place_address_county`),
  KEY `place_alias` (`place_alias`),
  KEY `place_audit_admin_id` (`place_audit_admin_id`),
  KEY `place_created_by_admin_id` (`place_created_by_admin_id`),
  FULLTEXT KEY `place_name` (`place_name`),
  FULLTEXT KEY `place_keyword_search` (`place_name`,`place_address_town`),
  FULLTEXT KEY `place_address_town` (`place_address_town`)
) ENGINE=InnoDB AUTO_INCREMENT=135167 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `place_category` (
  `place_category_id` int(11) NOT NULL AUTO_INCREMENT,
  `place_category_place_id` int(11) NOT NULL DEFAULT '0',
  `place_category_category_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`place_category_id`),
  UNIQUE KEY `place_category_place_id` (`place_category_place_id`,`place_category_category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=208987 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `category` (
  `category_id` int(11) NOT NULL AUTO_INCREMENT,
  `category_name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB AUTO_INCREMENT=168 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `review` (
  `review_id` int(11) NOT NULL AUTO_INCREMENT,
  `review_user_id` int(11) NOT NULL DEFAULT '0',
  `review_place_id` int(11) NOT NULL DEFAULT '0',
  `review_map_item_id` int(11) NOT NULL DEFAULT '0',
  `review_otm_item_name` varchar(156) DEFAULT NULL,
  `review_headline` varchar(255) DEFAULT NULL,
  `review_message` text,
  `review_rating` tinyint(1) NOT NULL DEFAULT '0',
  `review_datetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `review_edited_datetime` datetime DEFAULT NULL,
  `review_hidden` tinyint(1) NOT NULL DEFAULT '0',
  `review_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `review_status` set('pending','published','hidden','deleted') NOT NULL,
  PRIMARY KEY (`review_id`),
  KEY `review_map_item_id` (`review_map_item_id`),
  KEY `review_place_id` (`review_place_id`),
  KEY `review_user_id` (`review_user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `map_price` (
  `map_price_id` int(11) NOT NULL AUTO_INCREMENT,
  `map_price_item_id` int(11) NOT NULL DEFAULT '0',
  `map_price_label` varchar(50) DEFAULT NULL,
  `map_price_value` decimal(10,2) DEFAULT NULL,
  PRIMARY KEY (`map_price_id`),
  KEY `map_price_item_id` (`map_price_item_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5872 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

CREATE TABLE `county_list` (
  `county_id` int(11) NOT NULL AUTO_INCREMENT,
  `county_country_id` int(11) NOT NULL DEFAULT '0',
  `county_name` varchar(120) DEFAULT NULL,
  `county_alias` varchar(120) DEFAULT NULL,
  PRIMARY KEY (`county_id`),
  KEY `county_alias` (`county_alias`),
  KEY `county_country_id` (`county_country_id`)
) ENGINE=InnoDB AUTO_INCREMENT=142 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

2 个答案:

答案 0 :(得分:2)

看看以下几行:

LEFT JOIN map_section_item ON map_section_item_item_id = map_item_id

| 1 |简单| map_section_item |指数| NULL | map_section_item_section_id | 8 | NULL | 5330 |用在哪里;使用索引;使用连接缓冲区(块嵌套循环)| |

注意" 5330"。这意味着它必须搜索大约5330个项目才能找到所需的行。

使用简单的INDEX(map_section_item_item_id),它将直接转到所需的一行(或几行)。这将使查询运行得更快。

彼此重复JOIN,至少对于那些拥有"行" > 1。

为什么LEFT?是每个"对"表可选缺少数据?

副作用:不要用表名前缀所有内容;太杂乱了。

答案 1 :(得分:0)

对于MySQL,请尝试使用STRAIGHT_JOIN子句......

SELECT STRAIGHT_JOIN map_item_name
FROM map_item
LEFT JOIN ...

STRAIGHT_JOIN告诉MySQL按照我列出的顺序进行查询。这样它就会强制map_item作为主表,所有其余的作为查找辅助表...