在 SQL Server 2012 上使用带有空间索引的 STDistance 比使用 COS、SIN 和 ACOS 计算要慢,并给出椭圆形的结果
Posted
技术标签:
【中文标题】在 SQL Server 2012 上使用带有空间索引的 STDistance 比使用 COS、SIN 和 ACOS 计算要慢,并给出椭圆形的结果【英文标题】:Using STDistance with Spatial index on SQL Server 2012 is slower then using COS, SIN & ACOS Calculations and gives oval shaped results 【发布时间】:2015-08-25 13:30:21 【问题描述】:我在 SQL Server 2012 数据库中有一个包含 3.000.000 条记录的表。这些记录代表地图上的一个点。所有这些记录都有 x、y 坐标和地理点作为字段(x、y、geo)。 我需要计算距离某个点 10.000 米范围内的所有点。
查询编号1 我用:
DECLARE @point geography
DECLARE @rad float
SET @point = geography::STGeomFromText('POINT(51.2207099068778 4.39961050577564)', 4326);
SET @rad = 10000
SELECT count(1)
FROM t_mailbox WITH (INDEX(SIndx_t_mailbox_geo_MHHM_512))
WHERE
@point.STDistance(geo) <= @rad
结果:找到 273.346 个点需要 4 秒。在地图上绘制这些点会在地图上形成椭圆形。 这肯定是错误的,因为并非所有点都包含在结果中。
查询编号2 我使用:
declare @radius int = 10000
DECLARE @x float = 51.2207099068778
DECLARE @y float = 4.39961050577564
SELECT count(1)
FROM t_mailbox
WHERE
ACOS(COS(RADIANS(90-@x))*COS(RADIANS(90-x)) +SIN(RADIANS(90-@x)) *SIN(RADIANS(90-x))*COS(RADIANS(@y-y)))*6371000 <= @radius
结果:找到 564.547 个点需要 2 秒。在地图上绘制这些点会形成一个完美的圆形。
问题:
-
为什么使用 SPATIAL INDEX 和 STDistance 比使用 SIN、COS 和 ACOS 进行更复杂的查询要慢?
为什么会导致错误的椭圆形点集?
我做错了什么?
【问题讨论】:
第一个查询有SET @rad = 10000
,但第二个查询有declare @radius int = 20000
。这可能是您在第二个查询中获得更多行的原因吗?
抱歉,两个查询的半径都必须是 10.000 米。我改变了它的问题
如果两个查询中的半径均为 10000,我预计差异会更小。你在第二个查询中仍然得到这么多行(几乎是第一个的两倍)?
This link works perfectly for me
【参考方案1】:
地理数据绘制在球体表面上。这意味着它看起来与几何(平面)数据不同。
想象一下拿一个地球仪,在上面画一个点。然后拿一个指南针并围绕该点画一个圆圈。现在剥去地球上的皮肤。请注意,它不会平躺,要使其平整,您必须将其拉伸。现在大多数人这样做的方式是拉伸顶部和底部(北极/南极)并将其拉伸到与赤道相同的长度。这会让你画的圆圈变成一个水平方向大于垂直方向的椭圆。
现在您使用的公式适用于平面半径内的点。这意味着无论您在哪个纬度,您都假设两条经线之间的距离是相同的(距离北极 5 英尺,经度 90 度和 91 度之间的距离远小于赤道处的距离)。
在墨卡托投影地图上,这个公式将生成一个完美的圆形地图,但在地球仪上,它不是。希望这是有道理的。
至于你的速度问题: A:苹果和橘子,你在做不同的计算。 B:在不知道如何设置索引的情况下,很难进行分析,但地理索引无论如何都非常糟糕,它在像国家这样的非常大的地理区域上效果更好。
【讨论】:
这是正确答案,我喜欢“剥离”类比@hcaelxxam!来自我的 +1。【参考方案2】:虽然 hcaelxxam 完美地回答了“为什么”,但您可能离开STDistance()
会发现更好的性能。虽然并非总是如此,但我通常发现使用 STIntersects()
或 STWithin()
来表示距离会更好 - 你如何做到这一点非常简单!
尝试将您的查询更改为以下内容。我会对结果感兴趣:
DECLARE @point geography;
DECLARE @rad float = 10000;
SET @point = geography::STGeomFromText('POINT(51.2207099068778 4.39961050577564)', 4326).STBuffer(@rad); -- We're creating the "oval" here
SELECT count(1)
FROM t_mailbox WITH (INDEX(SIndx_t_mailbox_geo_MHHM_512))
WHERE
@point.STIntersects(geo) = 1
您可能还想尝试使用和不使用索引提示。有时,强制它会生成一个低效的查询计划。
【讨论】:
以上是关于在 SQL Server 2012 上使用带有空间索引的 STDistance 比使用 COS、SIN 和 ACOS 计算要慢,并给出椭圆形的结果的主要内容,如果未能解决你的问题,请参考以下文章
如何在临时 sql server 上使用带有 Robot Framework 的数据库库
使用 sqlalchemy 和 pyodbc 连接到 SQL Server 2012