Sql server 为什么SQL Server中的空间搜索速度比PostGIS慢?

Sql server 为什么SQL Server中的空间搜索速度比PostGIS慢?,sql-server,sql-server-2008,geospatial,spatial-index,Sql Server,Sql Server 2008,Geospatial,Spatial Index,我正在努力将一些空间搜索功能从带有PostGIS的Postgres迁移到SQL Server,我看到一些非常糟糕的性能,甚至是索引 我的数据大约有一百万个点,我想找出这些点中哪些在给定的形状内,因此查询如下: DECLARE @Shape GEOMETRY = ... SELECT * FROM PointsTable WHERE Point.STWithin(@Shape) = 1 如果我选择一个相当小的形状,我有时可以得到亚秒的时间,但如果我的形状相当大(有时是),我可以得到超过5分钟的时

我正在努力将一些空间搜索功能从带有PostGIS的Postgres迁移到SQL Server,我看到一些非常糟糕的性能,甚至是索引

我的数据大约有一百万个点,我想找出这些点中哪些在给定的形状内,因此查询如下:

DECLARE @Shape GEOMETRY = ...
SELECT * FROM PointsTable WHERE Point.STWithin(@Shape) = 1
如果我选择一个相当小的形状,我有时可以得到亚秒的时间,但如果我的形状相当大(有时是),我可以得到超过5分钟的时间。如果我在Postgres中运行相同的搜索,它们总是在一秒钟以下(事实上,几乎所有搜索都在200毫秒以下)

我在索引上尝试了几种不同的网格大小(全部高、全部中、全部低),每个对象有不同的单元格(16、64、256),无论我做什么,时间都保持相当恒定。我想尝试更多的组合,但我甚至不知道该往哪个方向走。每个对象有多个单元格?较少的一些奇怪的网格大小组合

我已经看过我的查询计划,他们总是使用索引,这根本没有帮助。我甚至在没有索引的情况下也尝试过,而且情况也不会更糟

在这方面有什么建议吗?我所能找到的一切都表明“我们不能给你任何关于索引的建议,只要尝试所有东西,也许一个就可以了”,但由于创建索引需要10分钟,盲目地这样做是一种巨大的时间浪费

编辑: 我也把这个贴在了。以下是他们要求提供的一些信息:

我能得到的最佳工作索引是:

CREATE SPATIAL INDEX MapTesting_Location_Medium_Medium_Medium_Medium_16_NDX
    ON MapTesting (Location)
 USING GEOMETRY_GRID
  WITH (
    BOUNDING_BOX = ( -- The extent of our data, data is clustered in cities, but this is about as small as the index can be without missing thousands of points
        XMIN = -12135832,
        YMIN = 4433884,
        XMAX = -11296439,
        YMAX = 5443645),
    GRIDS = (
        LEVEL_1 = MEDIUM,
        LEVEL_2 = MEDIUM,
        LEVEL_3 = MEDIUM,
        LEVEL_4 = MEDIUM),
     CELLS_PER_OBJECT = 256 -- This was set to 16 but it was much slower
  )
我在使用索引时遇到了一些问题,但这是不同的

对于这些测试,我使用with(INDEX(…)子句为我的每个索引(测试网格大小和每个对象的单元格的各种设置)运行了一个测试搜索(在我的原始帖子中列出的一个),并且没有任何提示。我还使用每个索引和相同的搜索形状运行了sp_help_space_geometry_index。上面列出的索引运行最快,在sp_help_spatial_geometry_索引中也被列为效率最高的索引

运行搜索时,我会获得以下统计信息:

(1 row(s) affected)
Table 'MapTesting'. Scan count 0, logical reads 361142, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'extended_index_592590491_384009'. Scan count 1827, logical reads 8041, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)

 SQL Server Execution Times:
   CPU time = 6735 ms,  elapsed time = 13499 ms.
我还尝试使用随机点作为数据(因为我无法给出真实数据),但结果表明,使用随机数据进行搜索速度非常快。这让我们相信,我们的问题在于网格系统如何处理我们的数据

我们的数据是整个州的地址,因此有一些非常高密度的区域,但大部分是稀疏数据。我认为问题在于网格大小的设置对两者都不起作用。如果网格设置为
,索引将在低密度区域返回太多的单元格,如果网格设置为
,网格在高密度区域是无用的(在
中等
,它没有那么差,但仍然不好)


我能够使用索引,但它没有帮助。每个测试都是在打开“show actual execution plan”(显示实际执行计划)的情况下运行的,它始终显示索引。

以下是有关SQL Server空间扩展以及如何确保有效使用索引的一些说明:

显然,如果规划者在解析期间不知道实际的几何图形,那么他很难构建一个好的计划。自动建议插入
exec sp_executesql

替换:

-- does not use the spatial index without a hint
declare @latlonPoint geometry = geometry::Parse('POINT (45.518066 -122.767464)')
select a.id, a.shape.STAsText() 
from zipcodes a 
where a.shape.STIntersects(@latlonPoint)=1
go
与:

我的直觉反应是“因为微软没有费心加快速度,因为它不是一个企业特性”。也许我是在玩世不恭


我也不知道为什么要从Postgres迁移。

我不熟悉空间查询,但这可能是一个参数化查询问题

尝试使用固定值(使用参数化查询执行速度较慢的值)编写查询(不使用参数)并运行它。将时间与参数化版本进行比较。如果速度快得多,那么问题是参数化查询


如果上面的速度快得多,那么我将使用嵌入字符串中的参数值动态构建sql字符串,这样您就可以删除导致问题的参数。

您是否正确设置了空间索引?您的边界框正确吗?所有的点都在里面吗?在您的情况下,网格的HHMM可能工作得最好(同样取决于布丁盒)

你能试着用sp\u help\u spatical\u geometry\u index看看有什么问题吗?

尝试使用过滤器操作,告诉我们您得到的性能值是多少?(它只执行主过滤器(使用索引),而不经过次过滤器(真正的空间操作))


您的设置有问题。空间确实是一个新特性,但也没那么糟糕

我相信STIntersects在使用索引时会更好地进行优化,它的性能会比Stin更好,尤其是对于较大的形状。

您可以尝试将其分解为两个过程:

  • 将候选人选择到临时表w/
    .Filter()
  • 查询候选人w/
    .STWithin()
  • 例如:

    (仅用减少I/O所需的实际列替换
    选择*


    这种微优化应该是没有必要的,但我以前看到过不错的性能改进。此外,你还可以通过(1)对(2)的比率来衡量指数的选择性。

    我刚刚花了一天时间研究了一个类似的问题。特别是,我们正在进行多边形中的点类型的查询,其中有一组相对较小的多边形,但每个多边形都很大且复杂

    对于多边形表上的空间索引,解决方案如下:

  • 使用“几何体自动栅格”而不是旧的MMLL等。这将提供8级索引,而不是旧的4级,并且设置是自动的。而且
  • 将“每个对象的单元数”设置为2000或4000。(考虑到默认值为16,猜测起来并不容易!)
  • 这产生了巨大的不同。它比默认配置中的空间索引快10倍,比没有索引快60倍
    -- this does use the spatial index without using a hint
    declare @latlonPoint geometry = geometry::Parse('POINT (45.518066 -122.767464)')
    exec sp_executesql 
    N'select a.id, a.shape.STAsText() 
    from zipcodes a 
    where a.shape.STIntersects(@latlonPoint)=1', N'@latlonPoint geometry', @latlonPoint
    go
    
    SELECT * INTO #this FROM PointsTable WHERE Point.Filter(@Shape) = 1
    SELECT * FROM #this WHERE Point.STWithin(@Shape) = 1