SQL Server空间数据STIntersects函数返回意外结果的技术咨询
为什么SQL Server Geography类型中,平面上在多边形边上的点STIntersects返回非预期结果?
这问题我之前也碰到过,核心原因是SQL Server里geography类型的特殊规则——它是基于**球面坐标系(WGS84,SRID 4326)**计算的,和我们直觉里的平面几何逻辑有很大差异,再加上你定义的多边形环方向不符合要求,才导致了这个意外结果。
具体原因拆解
Geography类型的环方向强制规则
SQL Server的geography类型遵循OGC标准,对多边形的顶点顺序(环方向)有严格要求:- 北半球的多边形,必须用逆时针方向的顶点顺序,才能表示你预期的“矩形内部区域”;
- 如果用了顺时针顺序,这个多边形会被自动解析为「整个地球表面减去这个矩形」的区域——相当于你定义的是一个“反多边形”。
你的多边形是顺时针环
你定义的@g1顶点顺序是:(-97.5 33.0) → (-97.5 34.0) → (-96.5 34.0) → (-96.5 33.0) → 起点,在平面上看是顺时针方向。所以@g1实际代表的是地球除了这个矩形之外的所有区域,此时点@g2虽然在平面矩形的边上,但对这个“反多边形”来说,它属于外部区域,STIntersects自然返回0。补充:球面精度的潜在影响
即使环方向正确,球面计算的浮点精度问题偶尔也会导致边缘点的判断误差,但你的情况核心问题还是环方向错误。
解决方法
这里给你三个可行的方案,根据你的实际需求选择:
方案1:调整多边形顶点顺序为逆时针
把顶点顺序反过来,让多边形在北半球符合逆时针要求:
DECLARE @g1 AS GEOGRAPHY; DECLARE @g2 AS GEOGRAPHY; DECLARE @g3 AS GEOGRAPHY; SET @g1 = GEOGRAPHY::STGeomFromText('POLYGON ((-97.5 33.0, -96.5 33.0, -96.5 34.0, -97.5 34.0, -97.5 33.0))', 4326); SET @g2 = GEOGRAPHY::STGeomFromText('POINT (-97.5 33.5)', 4326); SET @g3 = GEOGRAPHY::STGeomFromText('POINT (-98.0 35.0)', 4326); SELECT @g1.STIntersects(@g2); -- 现在返回1 SELECT @g1.STIntersects(@g3); -- 返回0
方案2:改用Geometry类型(平面几何)
如果你的业务不需要球面地理计算,只是平面上的空间判断,直接用geometry类型就行——它基于笛卡尔坐标系,没有环方向的限制,你的原始代码就能得到预期结果:
DECLARE @g1 AS GEOMETRY; DECLARE @g2 AS GEOMETRY; DECLARE @g3 AS GEOMETRY; SET @g1 = GEOMETRY::STGeomFromText('POLYGON ((-97.5 33.0, -97.5 34.0, -96.5 34.0, -96.5 33.0, -97.5 33.0))', 4326); SET @g2 = GEOMETRY::STGeomFromText('POINT (-97.5 33.5)', 4326); SET @g3 = GEOMETRY::STGeomFromText('POINT (-98.0 35.0)', 4326); SELECT @g1.STIntersects(@g2); -- 返回1 SELECT @g1.STIntersects(@g3); -- 返回0
方案3:用MakeValid()自动修正多边形
如果你不确定环方向是否正确,可以调用MakeValid()方法让SQL Server自动修正多边形的环方向和几何有效性:
DECLARE @g1 AS GEOGRAPHY; DECLARE @g2 AS GEOGRAPHY; DECLARE @g3 AS GEOGRAPHY; SET @g1 = GEOGRAPHY::STGeomFromText('POLYGON ((-97.5 33.0, -97.5 34.0, -96.5 34.0, -96.5 33.0, -97.5 33.0))', 4326).MakeValid(); SET @g2 = GEOGRAPHY::STGeomFromText('POINT (-97.5 33.5)', 4326); SET @g3 = GEOGRAPHY::STGeomFromText('POINT (-98.0 35.0)', 4326); SELECT @g1.STIntersects(@g2); -- 返回1 SELECT @g1.STIntersects(@g3); -- 返回0
内容的提问来源于stack exchange,提问作者Jason Richmeier
相关产品推荐
相关产品推荐

