如何通过where子句中的计算优化sql查询速度
How to optimize the sql query speed with calculation in where clause
在我的 table 中,我有大约 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 ;
请给我关于如何优化此查询的建议。
真正优化查询的方法很少。一种方法是添加附加条件:
where (max_x between A and B and max_y between C and D) and
. . .
问题是您不能在 max_y
和 max_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 查询使用与典型关系数据库数据不同的数据结构,这就是需要扩展的原因。
这是一个很难回答的问题。也许,我会尝试:
- 您的 select 中有 3 个 常量调用。 2 次调用
radians( "48.70877900" )
和 1 次调用 radians( "37.49893200" )
)。如果可能的话,我会尝试更改这些对常量值的调用。
- 您可以在 SELECT 中添加更多条件,例如,删除 map_x 或 map_y so far from origin,例如
abs( map_x - origin_x ) > MAX_X_DISTANCE
希望对您有所帮助
PD:试试
SELECT COUNT(*) AS `neighbours_count`
FROM
(SELECT radians( "48.70877900" ) rX, radians( "37.49893200" ) rY FROM DUAL) Rad,
house
WHERE
( 6371 *
acos(
cos( Rad.rX ) *
cos( radians( `map_y` ) ) * cos( radians( `map_x` ) -
Rad.rY ) + sin( Rad.rX
)
* sin( radians( `map_y` ) ) )
) <= 0.3
在我的 table 中,我有大约 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 ;
请给我关于如何优化此查询的建议。
真正优化查询的方法很少。一种方法是添加附加条件:
where (max_x between A and B and max_y between C and D) and
. . .
问题是您不能在 max_y
和 max_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 查询使用与典型关系数据库数据不同的数据结构,这就是需要扩展的原因。
这是一个很难回答的问题。也许,我会尝试:
- 您的 select 中有 3 个 常量调用。 2 次调用
radians( "48.70877900" )
和 1 次调用radians( "37.49893200" )
)。如果可能的话,我会尝试更改这些对常量值的调用。 - 您可以在 SELECT 中添加更多条件,例如,删除 map_x 或 map_y so far from origin,例如
abs( map_x - origin_x ) > MAX_X_DISTANCE
希望对您有所帮助
PD:试试
SELECT COUNT(*) AS `neighbours_count`
FROM
(SELECT radians( "48.70877900" ) rX, radians( "37.49893200" ) rY FROM DUAL) Rad,
house
WHERE
( 6371 *
acos(
cos( Rad.rX ) *
cos( radians( `map_y` ) ) * cos( radians( `map_x` ) -
Rad.rY ) + sin( Rad.rX
)
* sin( radians( `map_y` ) ) )
) <= 0.3