SQL Server中GEOGRAPHY计算距离必须按lat,lon,SRID顺序传参,SRID须显式指定4326;应预转换为geography列再计算,避免重复构造;必须建立空间索引且仅对GEOGRAPHY列有效;传参需统一SRID,禁用GEOMETRY类型。

SQL Server 中用 GEOGRAPHY 类型计算距离必须注意坐标顺序
SQL Server 的 GEOGRAPHY::Point() 函数要求参数顺序是 lat, lon, SRID,不是经度优先——这是最常翻车的地方。传反了会导致距离计算完全失真,比如北京坐标写成 GEOGRAPHY::Point(116.4074, 39.9042, 4326),实际会落在南大西洋附近。
- 纬度(
latitude)在前,范围 -90 到 90;经度(longitude)在后,范围 -180 到 180 - SRID 必须显式指定,
4326是 WGS84 标准,几乎所有地图 API 和 GPS 设备都用它 - 如果原始数据表里字段名是
lng/lat,在调用前务必交换顺序:GEOGRAPHY::Point(t.lat, t.lng, 4326)
存储过程中封装 STDistance() 要避免重复构造 geography 对象
在循环或 JOIN 场景中反复用 GEOGRAPHY::Point() 构造点对象,性能损耗明显。正确做法是在 CTE 或子查询里一次性转好,再参与计算。
- 错误写法:在 WHERE 或 SELECT 中多次调用
GEOGRAPHY::Point(lat, lng, 4326).STDistance(...) - 推荐写法:先用 CTE 把原始经纬度转为
geo列,后续只调用geo.STDistance(@ref) -
STDistance()返回单位固定为米,无需额外换算;但要注意 NULL 输入会直接导致整行结果为 NULL
WITH geo_points AS (
SELECT id, name,
GEOGRAPHY::Point(latitude, longitude, 4326) AS geo
FROM Locations
)
SELECT a.name, b.name,
a.geo.STDistance(b.geo) AS distance_m
FROM geo_points a
CROSS JOIN geo_points b
WHERE a.id < b.id;
大批量距离计算前必须加空间索引
没建空间索引时,STDistance() 在百万级数据上可能秒变分钟级响应。空间索引不是可选项,而是硬性前提。
- 索引必须建在
GEOGRAPHY类型列上,不能建在latitude/longitude普通 float 列上 - 创建语句是
CREATE SPATIAL INDEX SIdx_Locations_geo ON Locations(geo);,索引名建议带语义 - 空间索引对「范围查询」(如
STDistance() )加速显著,但对「两两全量计算」帮助有限——后者仍需算法剪枝
存储过程里传参计算距离时,别忽略 SRID 一致性
如果输入参数来自外部系统(比如前端传来的经纬度),而你的表里用的是 4326,但参数没指定 SRID,STDistance() 会静默失败或返回 0。
- 安全做法:所有输入点都显式包装为
GEOGRAPHY::Point(@lat, @lon, 4326),不依赖隐式转换 - 不要混用
GEOMETRY和GEOGRAPHY类型——前者是平面坐标,算出来的“距离”毫无地理意义 - 调试时可用
.Lat和.Long属性快速验证点是否构造成功:SELECT @p.Lat, @p.Long

















