ST_Distance_Sphere函数需MySQL≥8.0.16、POINT字段类型、用ST_GeomFromText插入坐标,返回单位为米;ThinkPHP中须用原生SQL+bind()防注入。

MySQL 的 ST_Distance_Sphere 函数怎么用才不出错
ThinkPHP 本身不处理地理距离计算,真正干活的是 MySQL 8.0+ 的空间函数。如果你用的是低版本 MySQL 或没开启空间扩展,ST_Distance_Sphere 直接报错 FUNCTION xxx.ST_Distance_Sphere does not exist。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 确认 MySQL 版本 ≥ 8.0.16(早期 8.0 版本可能不支持该函数)
- 字段类型必须是
POINT,且用ST_GeomFromText('POINT(116.4 39.9)')插入,不能存字符串或两个分离的lat/lng字段 - 查询时注意单位:返回值是「米」,不是公里,别忘了除以 1000 做展示
- 在 ThinkPHP 查询中,得用原生 SQL 或
query(),ORM 的where()不识别空间函数
ThinkPHP 中怎么安全传入用户坐标并防 SQL 注入
直接把前端传来的经纬度拼进 ST_Distance_Sphere 是高危操作——哪怕只是字符串拼接,也容易触发注入或语法错误。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
bind()绑定参数,例如:$db->query('SELECT * FROM shop WHERE ST_Distance_Sphere(location, ST_GeomFromText(?)) - 对输入做基础校验:
$lat必须在 -90 ~ 90,$lng在 -180 ~ 180,否则ST_GeomFromText返回NULL,整条 WHERE 失效 - 别信前端传的
location字段名,ThinkPHP 默认不会过滤它;自己重命名成user_lng/user_lat更可控
为什么加了空间索引还是慢?SPATIAL 索引的硬约束
SPATIAL 索引只加速 MBRContains、MBRWithin 这类最小边界矩形判断,对 ST_Distance_Sphere 无效——它仍会全表扫描。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 先用
ST_Within或MBRContains快速圈出一个粗略范围(比如 10 公里矩形),再在结果里用ST_Distance_Sphere精排 -
SPATIAL索引字段必须是NOT NULL,且只能建在单个POINT列上,不能复合 - 执行
EXPLAIN看key列是否显示你的空间索引名;如果显示NULL,说明没用上
Laravel 用户常问但 ThinkPHP 同样踩坑的坐标系问题
高德、百度、腾讯地图用的是 GCJ-02 或 BD-09 坐标系,而 MySQL 的 ST_Distance_Sphere 按 WGS-84(GPS 标准)算。两者混用,500 米内误差可达 200 米以上。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 入库前统一转 WGS-84:服务端调用高德逆地理 API 时加参数
coordtype=gcj02towgs84,或用开源库如eviltransform做离线转换 - 别依赖前端
Geolocation API直接上报的坐标——iOS Safari 返回的是 WGS-84,安卓微信内置浏览器可能是 GCJ-02 - 测试时拿已知精确坐标的 POI(比如天安门广场官方坐标 39.9042°N, 116.4074°E)比对查询结果,误差超 50 米就得查坐标系
空间查询真正麻烦的从来不是写几行 SQL,而是坐标来源、存储格式、索引策略、单位换算这四件事没对齐。少盯一个,结果就偏得没谱。



















