MySQL实战:用ST_Contains判断坐标点是否在多边形围栏内 标签MySQL、GIS、空间函数、GEOMETRY、电子围栏前言做地图相关业务电子围栏是非常经典的需求比如打卡校验、配送范围、区域告警核心逻辑就是给定一个经纬度坐标判断这个点是不是落在划定的多边形区域里面。很多同学第一想法是在代码里写算法判断点是否在多边形内逻辑复杂还要自己处理边界其实MySQL内置空间函数ST_Contains一行SQL就能搞定。注意本文使用GEOMETRY类型存储多边形围栏就是上一篇聊到的冷门空间类型。核心原理ST_Contains(geometryA, geometryB)含义几何A是否包含几何B放到围栏场景A 围栏多边形B 待检测坐标点返回 1 在围栏内返回 0 在围栏外。小提醒参数顺序不能写反ST_Contains(多边形, 点)写反结果永远是0。准备表结构我们建一张围栏表用GEOMETRY存放多边形同时设置SRID4326WGS84GPS/高德百度通用坐标系。CREATETABLEfence(idINTPRIMARYKEYAUTO_INCREMENT,fence_nameVARCHAR(100)COMMENT围栏名称,fence_polyGEOMETRYNOTNULLSRID4326COMMENT多边形围栏,SPATIALINDEXidx_fence(fence_poly)COMMENT空间索引提升查询性能);SRID4326 很关键MySQL8.0支持绑定坐标系避免平面坐标带来距离、包含关系计算偏差。插入多边形围栏WKT格式的多边形语法POLYGON((lng1 lat1, lng2 lat2, lng3 lat3, lng1 lat1))⚠️多边形必须首尾坐标闭合起点和终点一致否则会报错。我们构造一个简易矩形区域模拟一块业务围栏INSERTINTOfence(fence_name,fence_poly)VALUES(重庆渝北测试围栏,ST_GeomFromText(POLYGON((106.50 29.55,106.60 29.55,106.60 29.62,106.50 29.62,106.50 29.55)),4326));使用ST_Contains做点位判断现在检测点位POINT(106.55 29.58)这个坐标在上面的矩形围栏内部。SELECTid,fence_name,ST_Contains(fence_poly,ST_GeomFromText(POINT(106.55 29.58),4326))ASis_insideFROMfence;执行结果is_inside 1代表点在围栏内。再测试一个围栏外的点POINT(106.70 29.70)SELECTid,fence_name,ST_Contains(fence_poly,ST_GeomFromText(POINT(106.70 29.70),4326))ASis_insideFROMfence;结果is_inside 0点在围栏外面。常用扩展写法1. 只查询包含该点位的围栏业务最常用SELECT*FROMfenceWHEREST_Contains(fence_poly,ST_GeomFromText(POINT(106.55 29.58),4326))1;加上空间索引当围栏表量大的时候查询性能会明显提升。2. 同时返回可读WKT方便调试SELECTid,fence_name,ST_AsText(fence_poly)ASpolygon_wkt,ST_AsText(ST_GeomFromText(POINT(106.55 29.58),4326))ASpoint_wkt,ST_Contains(fence_poly,ST_GeomFromText(POINT(106.55 29.58),4326))ASis_insideFROMfence;容易踩的坑重点经纬度顺序经度在前纬度在后 POINT(lng, lat)很多人习惯先写纬度再写经度坐标完全错位判断结果全部错误POINT(经度,纬度)不是(纬度,经度)。多边形必须闭合POLYGON 最后一组坐标必须和第一组一模一样不闭合会抛出异常。SRID不匹配计算异常多边形和POINT的SRID必须一致不能一个4326一个默认0MySQL8.0会直接报错。参数顺序不能颠倒ST_Contains(点,多边形)永远返回0。记住大的几何在前小点在后。边界点判定如果坐标刚好落在多边形的边线上ST_Contains返回0如果你需要边界也算在内改用ST_Intersects。ST_Intersects点在内部 OR 在边界都返回1。举个边界点示例SELECTST_Contains(fence_poly,ST_GeomFromText(POINT(106.50 29.55),4326))ASis_contains,ST_Intersects(fence_poly,ST_GeomFromText(POINT(106.50 29.55),4326))ASis_intersectsFROMfence;MySQL版本限制5.7支持空间函数但SRID支持弱推荐MySQL8.0。5.6及更早版本不建议上生产做空间判断。什么时候适合这种方案什么时候不适合✅ 适合场景围栏数量不多需要简单快速判断点位归属不想额外引入GIS服务希望把逻辑放在数据库快速开发批量点位校验简单电子围栏打卡场景❌ 不推荐场景超大数据量几十万条围栏高频并发查询。MySQL空间索引能力有限高并发场景容易拖垮数据库。复杂GIS计算、大量多边形叠加、路网距离计算。这类场景更推荐PostGIS或者直接调用地图服务商API。业务需要复杂地图渲染一般会单独把WKT同步到Redis或者GIS引擎不会每次都查MySQL。业务落地小建议前端地图绘制围栏直接导出WKT字符串入库不用手动拼接坐标避免写错。开发环境一定要做边界点、跨边界点位测试区分 ST_Contains 和 ST_Intersects。空间索引不是万能不要指望百万级围栏表还能毫秒级查询。线上尽量不要在大查询里写复杂空间函数必要时做缓存。结尾ST_Contains是MySQL空间体系里非常实用的函数用GEOMETRY多边形存围栏一行SQL搞定点和面的包含判断省去手写点面算法的麻烦。但是也要理性看待它不是银弹。简单的电子围栏业务可以直接拿来用高并发复杂GIS业务还是交给专业GIS引擎更稳妥。思考题如果是圆形围栏根据中心点半径判断MySQL用什么空间函数实现