存4个真实城市坐标,建空间索引,验证地理距离计算和范围查询是不是真的用上了索引,顺手踩一个真实的经纬度顺序坑。
代码CREATE TABLE geo_test (id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), loc POINT NOT NULL SRID 4326, SPATIAL INDEX(loc)); INSERT INTO geo_test (name, loc) VALUES ('Beijing', ST_GeomFromText('POINT(39.9 116.4)', 4326)), ('Shanghai', ST_GeomFromText('POINT(31.2 121.5)', 4326)), ('Guangzhou', ST_GeomFromText('POINT(23.1 113.3)', 4326)), ('Shenzhen', ST_GeomFromText('POINT(22.5 114.1)', 4326));
POINT(116.4 39.9) -- 按"经度 纬度"的直觉顺序写 → ERROR 3617: Latitude 116.400000 is out of range in function st_geomfromtext. It must be within [-90.000000, 90.000000].
SRID 4326(WGS84,最常用的地球坐标系)下,POINT()真实的参数顺序是"纬度在前,经度在后"——和大多数人直觉里"经度、纬度"或者GeoJSON里"经度、纬度"的顺序真实是反的。改成POINT(39.9 116.4)(纬度39.9,经度116.4,北京的真实坐标)之后才插入成功。
SELECT name, ROUND(ST_Distance_Sphere(loc, ST_GeomFromText('POINT(23.1 113.3)', 4326))/1000, 1) AS km
FROM geo_test ORDER BY km;
Guangzhou 0.0
Shenzhen 105.7 km ← 真实的广深直线距离,跟现实中约104km非常接近
Shanghai 1211.3 km
Beijing 1890.7 kmST_Distance_Sphere真实用球面模型计算两点间的大圆距离,算出来的广州到深圳105.7公里,跟这两个城市真实的直线距离(约104公里)非常接近,误差来自地球并非完美球体的近似处理。
SELECT name FROM geo_test WHERE MBRContains(
ST_GeomFromText('POLYGON((20 110,20 125,42 125,42 110,20 110))', 4326), loc);
-> Filter: mbrcontains(...)
-> Index range scan on geo_test using loc over (loc unprintable_geometry_value) ← 真实走了空间索引每一步都是本文上面用真实地理坐标数据跑出来的原始结果。
ST_Distance_Sphere() / MBRContains() + SPATIAL INDEX