我可以避免在此查询中重复计算吗?

时间:2014-10-31 10:00:58

标签: mysql performance optimization

要匹配经纬度(point_lat, point_lon)点附近的点,我需要做类似的事情,

SELECT id, lat, lon,
ACOS(COS(RADIANS(ABS(lat - point_lat))) * COS(RADIANS(IF(ABS(lon - point_lon) > 180, 360 - ABS(lon - point_lon), ABS(lon - point_lon))))) as angle_between
FROM places;

查询的IF(ABS(lon - point_lon) > 180, 360 - ABS(lon - point_lon), ABS(lon - point_lon))部分确实让我大吃一惊,看起来ABS(lon - point_lon)计算了2次,除非MySQL在内部优化IF

places表非常大(约600万行),所以我希望尽可能高效地完成这项工作。你有什么建议吗?

编辑: ABS(lon - point_lon)计算成本不高,我知道。但事实上,我在查询中选择的angle_between需要像 angle_between = IF(angle_between > 180, 360 - angle_between, angle_between) 计算angle_between非常昂贵。

我知道我可以使用派生表,查询变为:

SELECT t.id, t.lat, t.lon, t.angle_between, if(t.angle_between > 180, 360 - angle_between, angle_between) as angle FROM (
    SELECT id, lat, lon,
    ACOS(COS(RADIANS(ABS(lat - point_lat))) * COS(RADIANS(IF(ABS(lon - point_lon) > 180, 360 - ABS(lon - point_lon), ABS(lon - point_lon))))) as angle_between
    FROM places
) AS t
ORDER BY angle LIMIT 20;

'自定义MySQL功能'似乎也是一种选择,但我对它们的性能不太确定。

1 个答案:

答案 0 :(得分:2)

我不担心为ABS(lon - point_lon)计算所花费的时间。这应该是计算的一小部分,尤其是在使用三角函数时。事实上,对于大多数查询而言,访问数据所花费的时间主导了查询;不是运行功能所花费的时间。情况并非总是如此,但在您的查询中,ACOS()和其他功能将花费更多时间。

如果您不想要明确的if(),您还可以使用:

greatest(ABS(lon - point_lon), 360 - ABS(lon - point_lon))

编辑:

在您的特定案例中,您可以执行以下操作:

IF( (@x := ABS(lon - point_lon)) > 180, 360 - @x, @x)

这只能保证有效,因为所有逻辑都在一个声明中(这就是为什么我没有先提出它)。 MySQL不保证select子句中表达式的评估顺序,因此您不应在任何其他表达式中使用@x

其他选项,例如使用子查询或union all,需要读取和写入更多数据。这是性能提升的一个很高的门槛(你总是可以测试替代方案,看看它们是否更好,只是不要过于乐观)。换句话说,没有真正好的解决方案,尽管变量方法可能最适合您的特定情况。