MySQL查询返回与csv存储坐标相比的最近位置沿着以米为单位的距离

rsaldnfx  于 2023-02-21  发布在  Mysql
关注(0)|答案(1)|浏览(163)
    • 我有两张表**

1.检查床位置
| 坐标|
| - ------|
| 28.08982880911016、-31.649963296195086|
1.餐桌位置
| 拉特|隆|地点|
| - ------|- ------|- ------|
| 28.08982880911016美元|-31.649963296195086|试验|
我想要一个MySQL(版本5.7.41兼容)查询,可以返回表1中的所有记录;但是,我不想显示存储的坐标值;相反,我想使用ST_Distance_Sphere或半正矢公式来检查每个坐标与最近的位置是否符合以下情况:
1.如果最近的值小于300米,则返回相应的地点值
1.如果最接近的值介于300米和1500米之间,则返回:"近"+对应处值+"~"+最近值(以米为单位)+"m"
1.如果最接近的值大于1500,则返回"N/A" 3。
请注意,我在表位置中有大约一百万条记录,因此我需要性能影响最小的最简单查询。
先谢了。

nr9pn0ug

nr9pn0ug1#

在所有locations和所有places之间执行距离计算将是非常昂贵的,因此我们将创建一个边界框,该边界框可以使用可用索引来获得places的子集,在该子集上执行距离计算。
我们正在构建一个略大于我们感兴趣的1500m半径的边界框(考虑到不准确性):

dist in degrees latitude = (dist / rEarth) * (180 / pi)
dist in degrees longitude = (dist / rEarth) * (180 / pi) / cos(latitude * pi/180)

dist = 1.550 km
rEarth ≈ 6371 km

dist in degrees latitude = (dist / rEarth) * (180 / pi)
dist in degrees latitude = (1.55 / 6371) * (180 / pi)
dist in degrees latitude = DEGREES(1.55/6371)
dist in degrees latitude = 0.013939

dist in degrees longitude = (dist / rEarth) * (180 / pi) / cos(latitude * pi/180)
dist in degrees longitude = 0.013939 / COS(RADIANS(latitude))

以位置为中心的3.100 km x 3.100 km边界框开始连接。坐标然后对找到的实体进行点到点距离计算:

SELECT *,
    /*
     * We are pre-splitting the coords here so we don't have to do
     * it repeatedly inside the correlated sub-query
     */
    SUBSTRING_INDEX(l.coords, ',', 1) AS loc_lat,
    SUBSTRING_INDEX(l.coords, ',', -1) AS loc_lon,
    (
        SELECT CONCAT(
                ROUND(ST_Distance_Sphere(
                    ST_GeomFromText(CONCAT('POINT(', REPLACE(l.coords, ',', ' '),')')),
                    ST_GeomFromText(CONCAT('POINT(', lat, ' ', lon,')'))
                ), -1),
                '::',
                place
            ) dist
        FROM places
        WHERE lat BETWEEN loc_lat - 0.013939 AND loc_lat + 0.013939
        AND lon BETWEEN loc_lon - (0.013939 / COS(RADIANS(loc_lat))) AND loc_lon + (0.013939 / COS(RADIANS(loc_lat)))
        HAVING dist <= 1500
        ORDER BY dist ASC
        LIMIT 1
    ) AS place,
    (
        SELECT
            CASE
                WHEN SUBSTRING_INDEX(place, '::', 1) < 300 THEN SUBSTRING_INDEX(place, '::', -1)
                WHEN SUBSTRING_INDEX(place, '::', 1) BETWEEN 300 AND 1500 THEN CONCAT('Near ', SUBSTRING_INDEX(place, '::', -1), '~', SUBSTRING_INDEX(place, '::', 1))
                ELSE 'N/A'
            END
    ) AS place_text
FROM locations l;

这个查询可以工作,但是它如何执行完全是另一回事。理想情况下,你应该有两个复合索引:(lat, lon)(lon, lat),但至少具有其中一个是至关重要的,否则您将不断地进行表扫描。

相关问题