如何在不使用几何类型的情况下获得MySQL中计算距离的更高精度?

How to get more precision for a computed distance in MySQL, without using geometric types?

提问人:JarsOfJam-Scheduler 提问时间:1/17/2019 最后编辑:JarsOfJam-Scheduler 更新时间:1/22/2019 访问量:114

问:

我必须计算一个对象(一个城市)与我拥有的MySQL表(一些餐馆)中的几个条目中的每一个之间的距离。这个城市和餐厅位于同一个国家。

计算出的距离用于显示靠近该城市的所有餐厅;阈值距离是任意的。此外,这是一个排名列表:最近的餐厅显示在最前面,最热门的餐厅显示在列表的末尾。我的问题是关于这个排名。

我现在做了什么

所以我做了一些研究,我成功地计算出了这个距离。

    $special_select_distance = "DEGREES(ACOS(COS(RADIANS(" . $oneVilles->__get('latitude')[app::getLang()] . ")) * COS(RADIANS(lat)) * COS(RADIANS(lon) - RADIANS(" . $oneVilles->__get('longitude')[app::getLang()] . ")) + SIN(RADIANS(" . $oneVilles->__get('latitude')[app::getLang()] . ")) * SIN(RADIANS(lat))))";

    $restaurants = $restaurantsDAO->getAll(null, ['distance DESC'] , null, 'HAVING distance < 1.9' , null , '*, ' . $special_select_distance . " AS distance");

...哪里:

  1. ['distance DESC']代表按距离排名

  2. 'HAVING distance < 1.9'代表任意阈值

  3. '*, ' . $special_select_distance . " AS distance"是选择器

  4. $oneVilles->__get('latitude')[app::getLang()]并且是城市的坐标纬度和纬度$oneVilles->__get('longitude')[app::getLang()]

  5. lat并且是餐厅的坐标(自动纳入我们正在迭代的表中,即:表,因为我们使用餐厅 DAO)lonrestaurants

问题

实际结果和意外结果

对于彼此之间非常接近的每家餐厅,计算出的与城市的距离保持不变。

示例:假设餐厅 A 和 B 非常接近。然后,A和城市之间的距离与B和城市之间的距离相同,这是我实际和意外的结果。

这不是我想要的。事实上,实际上,其中一家餐厅比另一家离城市最近。我认为MySQL中没有足够的精度。

预期结果

预期结果:根据与城市工作的距离对餐厅进行排名。换句话说,要获得更精确的计算距离。

示例:假设餐厅 A 和 B 非常接近。然后,A和城市之间的距离比B和城市之间的距离短,这是我预期的结果。

计算距离的示例

  1. 餐厅和城市之间(餐厅离城市很远):1.933156948976873

  2. 在餐厅 A 和城市之间(A 靠近城市):1.6054631070094885

  3. 在餐厅 B 和城市之间(B 靠近 A):1.6054631070094885

以点为单位的距离 2.和 3.是一样的,这是不正常的。我希望有更多的数字,以便能够更有效地对我的餐厅进行排名。

约束

  • 我不想更改MySQL服务器的配置。

    • 特别是:我绝对不能使用MySQL几何类型(这是公司的约束)
  • 如果可能的话,预期的解决方案应该只是更改我编写并提供给您的SQL查询,以便更精确。

  • 如有必要,允许使用其他计算距离的方法。

MySQL 精度 距离 经纬度

评论

0赞 Nico Haase 1/17/2019
您能解释一下这一切与“精度”有什么关系吗?你在哪里考虑餐厅之间的距离?通过一些示例数据和您发出的实际查询,这可能会变得更加清晰
0赞 JarsOfJam-Scheduler 1/17/2019
我在示例中给出的计算距离在点 2 中是相同的。和 3.然而,实际上,其中一家餐厅比另一家更靠近城市。例如,我应该有:对于 A(注意我添加的最后一位数字)和 B(注意我添加的最后一位数字)。所以 A 比 B 更接近城市,这种缺乏精确度是由于计算方法或存储,我不知道?最后:距离是 takan 帐户对餐厅进行排名(参见 SQL 查询,)。1.605463107009488511.60546310700948852getAll

答:

2赞 Rick James 1/18/2019 #1

对于长距离,请使用半正弦公式来确保准确性。对于短距离,毕达哥拉斯的速度是其两倍。

16 位有效数字(数据类型)是荒谬的。您无需区分狗身上的两种不同的跳蚤。DOUBLE

对于毕达哥拉斯,一定要将经度除以纬度的余弦——赫尔辛基附近的一度经度是赤道一度的一半。

更多细节在这里: http://mysql.rjweb.org/doc.php/latlng

如果是纬度差,那么可以这样想:如果你和我在同一经度,但我们的纬度是 1.605463 和 1.605464,那么,好吧,我不太了解你,无法那么接近。1.6054631070094885

在没有软糖因子的情况下比较两个浮点值是不切实际的:

If abs(a-b) < 0.00001, then treat them as equal.

更多

我推荐纬度、液化天然气和距离,因为你说的是餐馆。如果你谈论的不是超过 100 英里或公里,那么这个表达就足够精确了:FLOAT

SQRT(  ($lat - lat) *
       ($lat - lat) +
      (($lng - lng) * COS(RADIANS(lat))) *
      (($lng - lng) * COS(RADIANS(lat))) )  * $factor

哪里。。。

  • lat和 是表中列的名称,以度为单位。lngFLOAT
  • $lat并且是您开始的位置的值,也以度为单位。(PHP使用;其他语言使用其他约定。$lng$
  • $factor英里为 69.172 或公里为 111.325。
  • 我不会显示超过小数点后 1 位的结果。(不要显示“12.345678 英里”;“12.3英里”就足够了。

毕达哥拉斯和GCD的比较

             Pyt        GCD
To Rennes:  93.9407    93.6542
To Vannes:  95.6244    95.6241

评论

1赞 Rick James 1/20/2019
@JarsOfJam-Scheduler - 我添加了一些。
1赞 Rick James 1/21/2019
不要在数字中使用千位分隔符:。请参阅 ?...) * 111,325 AS distance ...325
1赞 Rick James 1/21/2019
如果我的公式正确的话,距离越小,餐馆离你在圣布里厄的位置越近。由于数字是公里,因此可能过多。您可能想要而不是 .400ORDER BY distance ASCDESC
1赞 Rick James 1/21/2019
是的,“半径”。或者“乌鸦飞翔”。892米很可能是1400米,在街道上蜿蜒曲折。但是获得步行距离远远超出了MySQL的范围。但是,我认为您可以使用 Google API。
1赞 Rick James 1/22/2019
@JarsOfJam-Scheduler - 您要求 400(半径)公里内的所有餐厅;瓦讷距离酒店仅有100公里。雷恩更近一些;它是“可见的”吗?检查。。。