如何通过where子句中的计算来优化sql查询速度

时间:2015-09-24 13:40:18

标签: mysql sql query-optimization

在我的表中,我有大约300万条记录。 当我运行此查询时,获取计数值需要大约15-30秒

SELECT COUNT(*) AS `neighbours_count` FROM house 
WHERE 
    ( 6371 * 
          acos( 
              cos( radians( "48.70877900" ) ) * 
              cos( radians( `map_y` ) ) * cos( radians( `map_x` ) - 
              radians( "37.49893200" ) ) + sin( radians( "48.70877900" ) 
          ) 
          * sin( radians( `map_y` ) ) ) 
    ) <= 0.3

查询本身会计算特定坐标300米范围内的建筑物。 6371是地球半径,其余部分是公式计算接近坐标的公式。

查询解释

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  house   ALL     NULL    NULL    NULL    NULL    2442710     Using where

创建声明

CREATE TABLE IF NOT EXISTS `house` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `country_id` int(11) NOT NULL,
  `state_id` int(11) NOT NULL,
  `city_id` int(11) NOT NULL,
  `street_id` int(11) NOT NULL,
  `name` varchar(250) NOT NULL,
  `map_x` decimal(11,8) NOT NULL,
  `map_y` decimal(11,8) NOT NULL,
  UNIQUE KEY `id` (`id`),
  KEY `country_id` (`country_id`),
  KEY `city_id` (`city_id`),
  KEY `street_id` (`street_id`),
  KEY `map_x` (`map_x`),
  KEY `map_y` (`map_y`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COMMENT='map_y - latitude, map_x - longitude' AUTO_INCREMENT=2442769 ;

请给我一个如何优化此查询的建议。

2 个答案:

答案 0 :(得分:0)

真正优化查询的方法很少。一种方法是添加其他条件:

where (max_x between A and B and max_y between C and D) and
      . . .

问题在于您无法在max_ymax_x上使用索引。一种变化是在每侧施加300米的网格并将每个点移动到最近的网格点。这实现起来有点痛苦(需要触发器或类似的东西)。但是你知道你的状况意味着相邻的网格点上的东西。所以,像这样:

where (grid_x, grid_y) in ((grid_A-1, grid_B), (grid_A-1, grid_B-1), (grid_A-1, grid_B+1),
                           (grid_A, grid_B), (grid_A, grid_B-1), (grid_A, grid_B+1),
                           (grid_A+1, grid_B), (grid_A+1, grid_B-1), (grid_A+1, grid_B+1)
                          ) and
       . . .

这可以使用grid_x, grid_y上的索引 - 我想。如果没有,您可以使用union all获得相同的效果。

真的,最好的选择是geospatial extensions。 GIS查询使用来自典型关系数据库数据的不同数据结构,这就是需要扩展的原因。

答案 1 :(得分:0)

这是一个困难的答案。也许,我会尝试:

  • 您的选择中有3个常量调用。 2次致电mysql> describe web_admin_roles; +----------------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------------------+-------------+------+-----+---------+-------+ | roleIdentity | varchar(64) | NO | PRI | NULL | | | addNewAdmin | bit(1) | YES | | NULL | | | viewAdmin | bit(1) | YES | | NULL | | | modifyAdmin | bit(1) | YES | | NULL | | | removeAdmin | bit(1) | YES | | NULL | | | approveUser | bit(1) | YES | | NULL | | | viewUser | bit(1) | YES | | NULL | | | modifyUser | bit(1) | YES | | NULL | | | removeUser | bit(1) | YES | | NULL | | | addFacility | bit(1) | YES | | NULL | | | viewFacility | bit(1) | YES | | NULL | | | modifyFacility | bit(1) | YES | | NULL | | | removeFacility | bit(1) | YES | | NULL | | | addManager | bit(1) | YES | | NULL | | | viewManager | bit(1) | YES | | NULL | | | modifyManager | bit(1) | YES | | NULL | | | removeManager | bit(1) | YES | | NULL | | | isSuperAdmin | bit(1) | NO | | NULL | | | createdTimeMilliSecs | bigint(20) | NO | | NULL | | | updatedTimeMilliSecs | bigint(20) | YES | | NULL | | | isRecordActive | int(11) | NO | | NULL | | +----------------------+-------------+------+-----+---------+-------+ ,1次致电radians( "48.70877900" ))。如果可能的话,我会尝试更改这些常量值的调用。
  • 您可以在SELECT中添加更多条件,例如,将map_x或map_y从原点移除,例如radians( "37.49893200" )

我希望你会发现它有用

PD:尝试

abs( map_x - origin_x ) > MAX_X_DISTANCE