应使用st_contains(polygon, point)判断点是否在多边形内,确保srid一致、几何有效,并为geom字段建立gist/spatial索引;避免between和mbrcontains等不精确方法。

WHERE 条件里用 ST_Contains 判断点是否在多边形内
直接用经纬度范围更新标记,本质是「把落在某地理区域内的点找出来并打标」。别用 BETWEEN 做矩形框筛选——它不处理地球曲率,也不支持任意形状(比如行政区划边界)。PostGIS 或 MySQL 8.0+ 的地理空间函数才是正解。ST_Contains 是最常用且语义清晰的选择:第一个参数是多边形(区域),第二个是点(标记位置)。
常见错误是把参数顺序搞反,写成 ST_Contains(point, polygon),会返回空或报错。正确写法必须是 ST_Contains(polygon, point)。另外,确保点和多边形的 SRID 一致(如都用 4326),否则结果不可靠。
- PostgreSQL/PostGIS 示例:
UPDATE markers SET region_tag = 'downtown' WHERE ST_Contains(ST_PolygonFromText('POLYGON((116.35 39.90, 116.38 39.90, 116.38 39.93, 116.35 39.93, 116.35 39.90))', 4326), geom); - MySQL 8.0+ 示例:
UPDATE markers SET region_tag = 'downtown' WHERE ST_Contains(ST_PolygonFromText('POLYGON((116.35 39.90, 116.38 39.90, 116.38 39.93, 116.35 39.93, 116.35 39.90))'), geom); - 如果区域来自另一张表(如
regions),用 JOIN 更安全:UPDATE markers m JOIN regions r ON ST_Contains(r.boundary, m.geom) SET m.region_tag = r.name WHERE r.code = 'BJ-DT';
用 ST_MakeEnvelope 快速生成经纬度矩形范围
如果你手头只有左下角(min_lng, min_lat)和右上角(max_lng, max_lat)四个数值,别手动拼 POLYGON 字符串——易错、难读、还可能因坐标顺序导致无效几何体。PostGIS 提供 ST_MakeEnvelope,MySQL 有 ST_MakeEnvelope(8.0.29+)或退而求其次用 ST_Rectangle(需注意版本兼容性)。
关键点:PostGIS 中经纬度顺序是 min_lng, min_lat, max_lng, max_lat(即 x-min, y-min, x-max, y-max),不是 lat-lng;MySQL 同样按 X-Y(经度-纬度)顺序。传反了会导致范围为空或跨国际日期变更线。
- PostGIS 示例(带 SRID):
ST_MakeEnvelope(116.35, 39.90, 116.38, 39.93, 4326)
- MySQL 示例(无显式 SRID,默认 0,但字段需定义为
SRID 4326):ST_MakeEnvelope(116.35, 39.90, 116.38, 39.93)
- 性能提示:对
geom字段建GIST索引(PostGIS)或SPATIAL索引(MySQL),否则ST_Contains会全表扫描。
MySQL 5.7 没有 ST_Contains?用 MbrContains 临时替代但要小心
MySQL 5.7 不支持真正的地理空间谓词,ST_Contains 只对平面坐标有效,且要求 geometry 类型字段明确声明 SRID(5.7 实际不校验)。这时候只能退用 MbrContains——但它判断的是最小外接矩形(MBR),不是真实形状。一个点明明在多边形凹陷处,也可能被误判为“不在内”。
更糟的是,MbrContains 对 POINT 和 POLYGON 的参数顺序和 ST_Contains 相反:它是 MbrContains(polygon, point),看起来一样,但底层逻辑不同。一旦升级到 8.0,必须全面替换,否则行为突变。
- 5.7 兼容写法(仅限简单矩形):
UPDATE markers SET region_tag = 'downtown' WHERE MbrContains(ST_GeomFromText('POLYGON((116.35 39.90, 116.38 39.90, 116.38 39.93, 116.35 39.93, 116.35 39.90))'), geom); - 强烈建议:只要环境允许,优先升级 MySQL 或迁移到 PostGIS。5.7 的地理空间能力本质是“能跑”,不是“能准”。
更新前务必验证几何有效性与坐标系
很多失败不是函数写错,而是数据本身有问题:geom 字段存的是 WKT 字符串没转 geometry、坐标反了(lat/lng 当成 lng/lat)、或者多边形自相交导致 ST_Contains 返回 NULL(即不匹配任何行)。
执行 UPDATE 前,先用 ST_IsValid 和 ST_SRID 检查样本数据。尤其注意:中国 GCJ-02 坐标系不能直接当 WGS84(4326)用,混用会导致偏差几百米——标记批量漂移到隔壁区。
- 检查有效性:
SELECT id, ST_IsValid(geom), ST_SRID(geom) FROM markers LIMIT 5;
- 修复无效几何(PostGIS):
UPDATE markers SET geom = ST_MakeValid(geom) WHERE NOT ST_IsValid(geom);
- 转换坐标系(如从 GCJ-02 转 WGS84)不能靠 SQL 函数完成,必须用专业库(如 proj4 或 Python 的 pyproj)预处理。
ST_IsValid 和 EXPLAIN,比事后调半天逻辑强得多。











