要匹配经纬度(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功能'似乎也是一种选择,但我对它们的性能不太确定。
答案 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
,需要读取和写入更多数据。这是性能提升的一个很高的门槛(你总是可以测试替代方案,看看它们是否更好,只是不要过于乐观)。换句话说,没有真正好的解决方案,尽管变量方法可能最适合您的特定情况。