优化 sql 查询以提高效率

Posted

技术标签:

【中文标题】优化 sql 查询以提高效率【英文标题】:Optimizing an sql query for efficiency 【发布时间】:2016-04-01 22:23:00 【问题描述】:

我有一个关于地理距离的查询。查询速度非常快,在我的 224 万行表上大约在 0.1175 秒内返回。但是,我只需要最低距离,使用内置 order by 太慢了。

有什么方法可以跟踪运行的最小值并直接给我吗?

例如,如果我有这些结果:

city a - 45km
city b - 48km
city c - 12km 

我可以让它给我 12 公里,记住所有的距离值都是计算出来的吗?

这是排序的查询:

SELECT 
City, 
( 
    6371 * 
    acos(
        cos(radians(-60.61384878636903)) * 
        cos(radians(st_x(location))) * 
        cos(radians(st_y(location)) - 
        radians(112.80061386895574)) + 
        sin(radians(-60.61384878636903)) * 
        sin(radians(st_x(location))))
    ) as distance 
FROM table_name 
HAVING distance <  5 
ORDER BY distance ASC LIMIT 1 

表结构如下:

id - int(12)
location - Point()
City - varchar(255) 

问题在于按标志排序需要太长时间才能对数据进行排序并获得最低值。它可以保持最低运行,然后在没有重大性能影响的情况下给我吗?

表格包含,

2227851 - rows
spatial index on location

如果我使用 order by,我得到的运行时间大约是 14 秒,如果我不使用 order by,我得到的时间是 0.1 秒,这是我想要的运行时间或接近它

【问题讨论】:

你有没有想过对方程做一些代数来简化它?例如,至少只需为 `cos(radians(-60.61384878636903))` 设置一个常量值 只是为了好玩,你试过WHERE 6371 * acos...。您还可以通过排除半径为 5 的边界矩形(嗯,正方形)之外的值来过滤数据集。 性能问题应该包括EXPLAIN ANALYZE和一些关于表大小、索引、当前时间性能、期望时间等的信息。Slow是一个相对术语,我们需要一个真实的值来比较。 mysql 为了返回这个查询的结果,数据库需要计算所有的行,然后找到所有距离小于5的记录,然后得到最小值。您是否尝试过使用 MIN 而不是排序?或者是否有任何逻辑可以仅使用“位置”字段找到最小距离? 回应 Ed Heal 中的数字是用户输入的纬度/经度度数,所以在这种情况下我使用了占位符。 【参考方案1】:

建议:

使用 MySql spatial functions 和 Index 带来更好的性能

但如果你不想使用它

预计算常量Set A = cos(radians(-60.61384878636903)) cos 函数非常慢。 过滤您的样本数据。如果您的起点是X,Y,您可以创建一个正方形X +- 5, Y +- 5 并在X,Y 上使用常规索引

【讨论】:

所以对于正方形来说,真的很难判断正方形是否包含答案,而对性能的真正影响是获得最小距离,这是我在任何提示后得到的值? 为什么很难?如果您在(0,0) 有一个点,那么比(-5,0) 剩下的任何东西都将是&gt; 5 distance ,因此您过滤的数据将不符合您的条件。当然,您需要将5 转换为十进制度,这样您就可以做到X +- DeltaX【参考方案2】:

不,实际上没有办法在查询级别保持运行最小值。

基本问题是,对于不同的纬度和经度值,最小距离会有所不同,搜索坐标在查询中以文字形式提供。

一种选择是保留以前的搜索坐标表,即您以前查找过的坐标表,然后使用它来缩短执行另一个查询的需要。首先搜索以前的搜索表,然后从那里得到结果。

当然,如果您向table_name 添加一行,您可能需要针对新行重新评估已保存的搜索坐标,并确定新行是否比您保存的距离更短。 (或者只是使整个商店无效,并为您执行的每次搜索重新填充。)


基本问题是必须为table_name 中的每一行计算“大圆距离”表达式。

对于不同的搜索值,该表达式的结果会有所不同 (纬度和经度)。

没有办法绕过对每一行进行计算,并在所有这些结果中找到最低值。这将是一个“使用文件排序”操作。使用 LIMIT 1,我们希望 MySQL 不必对整个集合进行排序,并且只需通过一次即可识别最小值。

如果您可以限制 table_name 中需要评估的行数,并且可以使用索引有效地排除它们...这样可以加快查询速度。

限制行数的一种方法是根据搜索纬度和经度定义一个“边界框”。并在 WHERE 子句中指定。并让 MySQL 使用适当的索引。最粗略的边界框可以定义为搜索坐标的 +/-dx 纬度和 +/-dy 经度...例如

 WHERE  t.lat BETWEEN  -60.613848 -4 AND -60.613848 +4 
   AND  t.lon BETWEEN  120.800613 -8 AND 120.800613 +8 

这不是一个理想的边界框,因为经度在赤道的距离比在两极附近的距离要长得多。


就保持“最低限度运行”而言...您当前的查询无法做到这一点。如果没有其他一些以某种方式键入搜索参数的数据存储,它就无法完成。

【讨论】:

这个表格是固定大小的,它包含了所有的城市,它永远不会包含新的城市。 如果您正在寻找特定州、地区、国家或其他任何地方最近的城市,您可以在 WHERE 子句中的谓词中使用它(以及适当的索引)来过滤out 可以排除的行。但是(显然)这可能不适合您的用例,并且需要向the_table 添加和填充列。并且在旅行时间或距离方面找到“乌鸦飞翔”的最小距离并不一定会找到“最近”。

以上是关于优化 sql 查询以提高效率的主要内容,如果未能解决你的问题,请参考以下文章

记录一个sql优化后大大提高查询效率的情形

php 代码优化提高MySQl的运行效率

如何提高sql2000全文检索搜索效率

如何做SQL优化?优化SQL语句,提高SQL查询效率。Effective MySQL之SQL语句最优化

优化我的 T-SQL 查询以提高性能

sql语句提高数据库查询效率