sql无内置地理聚类函数,需依赖postgis等空间扩展;常用方法包括geohash/h3网格分组或st_dwithin邻近合并,而非直接运行k-means。

SQL里没有内置的地理聚类函数,得靠空间扩展或预处理
PostgreSQL + PostGIS、MySQL 8.0+ 或 SQL Server 的空间功能可以支撑基础地理聚合,但原生 SQL 不提供 ST_ClusterKMeans 之外的聚类算法(且该函数仅 PostGIS 3.2+ 支持)。多数场景下,你实际要做的不是“在SQL里跑K-Means”,而是用空间关系做分组近似——比如按网格、行政区域或距离阈值归并点。
用地理网格(GeoHash或H3)做轻量级聚类最实用
直接对经纬度字段 GROUP BY ROUND(lat, 3), ROUND(lng, 3) 粗暴又不准;改用地理编码更可靠。PostGIS 中可用 ST_GeoHash(geom, 6) 生成6位 GeoHash 字符串,大致对应 ~1.2km × 0.6km 区域;H3 更优(需 h3-pg 扩展),例如:
SELECT h3_latlng_to_cell(lat, lng, 7) AS h3_7, COUNT(*) FROM locations GROUP BY h3_7;
注意:h3_latlng_to_cell 第三个参数是分辨率(5~15),7 级约 110km²,9 级约 1.7km²——选太高会导致碎片化,太低则失去区分度。
用 ST_DWithin 实现邻近点合并(需空间索引!)
想找出“500米内有多少个POI”,不能写 WHERE ST_Distance(a.geom, b.geom) ——这会全表扫描。必须用 <code>ST_DWithin 触发空间索引:
-
ST_DWithin(a.geom, b.geom, 500)单位是 SRID 对应单位(如 WGS84 是度,需先转为geography类型) - 确保
geom列有 GIST 索引:CREATE INDEX idx_locations_geom ON locations USING GIST(geom); - 若查每个点的邻近簇数量,用自连接 +
GROUP BY,但数据量大时性能陡降
PostGIS 3.2+ 的 ST_ClusterDBSCAN 是真聚类,但别滥用
它能在服务端跑密度聚类,参数敏感:
eps(邻域半径)和 minpoints(核心点最小邻点数)稍调就结果迥异。示例:
SELECT *, ST_ClusterDBSCAN(geom, eps := 0.005, minpoints := 3) OVER() AS cluster_id FROM locations;
这里 eps := 0.005 是度(WGS84),约 550 米;若用 geography 类型,就得写 eps := 500(单位米)。没建空间索引时,这个查询可能比应用层聚类还慢。
真正容易被忽略的是坐标系——所有距离类函数默认按平面坐标算,WGS84 经纬度直接传 eps 值会严重失真。要么转 geography,要么用 Web Mercator(SRID 3857)并确认单位是米。










