mysql 的 st_distance_sphere 函数可直接用于 where 子句筛选附近点,但需配合空间索引(如 r-tree)提升性能;其参数为 point(longitude, latitude),返回单位为米的球面距离。

MySQL 的 ST_Distance_Sphere 能直接查附近点,但必须用空间索引
直接在 WHERE 里写 ST_Distance_Sphere(point_col, POINT(lng, lat)) 看似简单,实际会全表扫描——哪怕你给 <code>point_col 加了 SPATIAL 索引也无效。MySQL 只有在使用 MBRContains、MBRWithin 或 ST_Contains 等能触发「最小边界矩形(MBR)预筛选」的函数时,才会走空间索引。
正确做法是先用 ST_Distance_Sphere 做精确距离计算,但把它放在 HAVING 或子查询中;外层用 MBRContains 快速圈出候选范围:
SELECT id, name, ST_Distance_Sphere(location, POINT(116.48, 39.92)) AS dist
FROM shops
WHERE MBRContains(
LineString(
Point(116.48 - 0.02, 39.92 - 0.02),
Point(116.48 + 0.02, 39.92 + 0.02)
),
location
)
HAVING dist <p>这里 <code>0.02</code> 是粗略换算(约 ±2.2 km),确保 MBR 覆盖目标半径,又不过大导致候选过多。</p><h3>PHP 中构造 POINT 字段不能拼字符串,得用 <code>GeomFromText</code> 或 <code>ST_PointFromText</code>
</h3><p>很多人习惯在 PHP 里拼 SQL:<code>"POINT($lng $lat)"</code>,这是危险且不可靠的——坐标含负号、小数精度、空格处理都可能出错,更严重的是会引入 SQL 注入漏洞。</p><p>安全写法是交给 MySQL 解析:</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站"><img
src="https://img.php.cn/upload/skill/000/000/081/179040786932301.jpg" alt="btpanel phpsite 宝塔面板PHP网站" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站" class="overflowclass">btpanel phpsite 宝塔面板PHP网站</a>
<p class="overflowclass">宝塔面板 PHP 网站管理:站点创建、删除、启停、PHP 版本切换、域名管理、SSL证书管理、伪静态管理、数据库管理</p>
</div>
<a rel="nofollow" href="/xiazai/skill5233" title="btpanel phpsite 宝塔面板PHP网站" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 用预处理语句传入两个浮点参数,再在 SQL 中调用
ST_PointFromText(CONCAT('POINT(', ?, ' ', ?, ')')) - 或更推荐:在建表时把经纬度存为两个
DOUBLE字段(lng,lat),再用生成列自动转 POINT:location POINT AS (POINT(lng, lat)) STORED,这样插入时只管填lng/lat,查询也干净
注意:生成列 + 空间索引要求 MySQL ≥ 5.7.6,且表引擎必须是 InnoDB。
ST_Distance_Sphere 返回单位是米,但精度受椭球模型影响
它默认用 WGS84 椭球体计算,结果比平面近似(如 Haversine)更准,尤其跨纬度大时。但要注意:
- 输入坐标顺序是
POINT(longitude latitude),反了会查到地球对面 - 如果数据源用的是 GCJ-02(国内火星坐标系),直接套用会偏移 300–500 米,必须先纠偏再入库
- 批量查时别在循环里反复执行
ST_Distance_Sphere——它 CPU 开销不小,10 万行数据单次计算可能超 1 秒
空间索引不支持 LIKE、ORDER BY 非空间字段等常见操作
加了 SPATIAL 索引后,别指望它能加速 WHERE name LIKE '%咖啡%' 或 ORDER BY created_at。空间索引只服务于空间关系判断(相交、包含、距离预筛)。
真实业务中往往要「按距离近 + 按评分高 + 按营业中」排序,这时得组合策略:
- 先用
MBRContains快速捞出几千个地理邻近的 ID - 再用这些 ID 在主键索引上 IN 查询,补全其他字段并做二次过滤和排序
- 必要时把常用筛选条件(如状态、分类)冗余到同一张表,避免多表 JOIN 拖垮空间查询优势
空间索引不是银弹——它只解决“我在哪片区域里”,剩下的事,还得靠传统索引和应用层协同。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










