改善STIntersects的性能


11

表格T_PIN有300,000个图钉和T_POLYGON36,000个多边形。T_PIN具有此索引:

CREATE SPATIAL INDEX [T_PIN_COORD] ON [dbo].[T_PIN]
(
[Coord]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, 
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY];

T_POLYGON 有:

CREATE SPATIAL INDEX [T_POLYGON_COORD] ON [dbo].[T_POLYGON]
(
[COORD]
)USING  GEOGRAPHY_GRID 
WITH (GRIDS =(LEVEL_1 = HIGH,LEVEL_2 = HIGH,LEVEL_3 = HIGH,LEVEL_4 = HIGH), 
CELLS_PER_OBJECT = 128, PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, 
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 
ON [PRIMARY];

查找的交集T_PINT_POLYGON花费超过45分钟才能执行的查询:

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1;

结果是4,438,318行。

如何加快此查询的速度?


您是否尝试过使用“ T_POLYGON.Coord.STIntersects(T_PIN.COORD)= 1”?
travis

我将有兴趣查看您的查询计划。我在Postgres上工作,但必须运行类似的查询,但是要处理大得多的数据集。我想出了一种技术,可以将最糟糕的情况减少到大约2天(不幸的是涉及脚本编写),但是我很想先看看您的查询计划。
约翰·鲍威尔

将两个表中的多边形相乘,就潜在交叉点的数量而言,我的数字比组合中的多边形大7000倍,因此,我认为这两天看起来还不错。但是,如果不查看查询计划并且不了解每个多边形的平均点数,则很难提出具体的解决方案。
约翰·鲍威尔

Answers:


7

首先,通过查看查询执行计划来检查是否正在使用空间索引,并查看是否存在“聚集索引寻求(空间)”项。

假设正在使用它,您可以尝试添加一个基于带有简化多边形的边界框的辅助/简化滤镜,以进行首次检查。然后可以通过主要过滤器对这些简化的多边形进行匹配以获得最终结果。

1)在[dbo]。[T_POLYGON]表中添加新的地理和几何列:

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeom geometry;
ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog geography;

2)创建边界框多边形(这涉及到初步转换为几何以利用STEnvelope()的优势):

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeom = geometry::STGeomFromWKB(
    COORD.STAsBinary(), COORD.STSrid).STEnvelope();

UPDATE [dbo].[T_POLYGON] SET SimplePolysGeog = geography::STGeomFromWKB(
    SimplePolysGeom.STAsBinary(), SimplePolysGeom.STSrid);

3)在简化的地理列上创建空间索引

4)获取与此简化的地理列相对的交点,然后再次对匹配的地理数据类型进行过滤。大概是这样的:

;WITH cte AS
(
   SELECT pinID, polygonID FROM T_PIN INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.SimplePolysGeog ) = 1
)
SELECT COUNT(*)
FROM T_PIN 
INNER JOIN T_POLYGON
    ON T_PIN.Coord.STIntersects(T_POLYGON.COORD) = 1
    AND T_PIN.pinID IN (SELECT pinID FROM cte)
    AND T_POLYGON.polygonID IN (SELECT polygonID FROM cte)

编辑:您可以用此计算的持久化列替换(1)和(2)。感谢Paul White的建议。

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS  ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),[COORD].[STSrid]).STEnvelope().STAsBinary(),(4326))) PERSISTED

是的,那差不多就是我要说的。我在空间上“连接”两组具有广泛覆盖范围的表时发现的问题是,优化器通常会进行两次全表扫描和多边形测试中的点负载。
约翰·鲍威尔

2

由于多边形的复杂性,此类查询通常会花费很长时间。我见过复杂的海岸线(例如)需要很长时间才能测试靠近边界的点,因此必须缩放许多级别才能找到一个点在内部还是外部。

...因此您可以尝试.Reduce()对多边形进行处理,以查看是否有帮助。

有关该功能的更多信息,请参见http://msdn.microsoft.com/zh-cn/library/cc627410.aspx


1

根据Microsoft文档,当空间索引出现在带有WHERE子句的比较谓词的开头时,它们将与以下方法的地理类型一起使用:

  • STIntersects
  • STDistance
  • STEquals

仅几何类型的方法(受限列表)将触发中的空间索引的使用JOIN ... ON,因此,请更改要使用的代码WHERE geog1.STIntersects(geog2) = 1,这样可以提高速度。

我还建议在g2server的答案中提出建议,并添加以下内容进行过滤并在其上添加空间索引

ALTER TABLE [dbo].[T_POLYGON] ADD SimplePolysGeog AS
     ([geography]::STGeomFromWKB([geometry]::STGeomFromWKB([COORD].[STAsBinary](),
                                                           [COORD].[STSrid])
                 .STEnvelope().STAsBinary(),(4326))) PERSISTED

然后,您可能会遇到类似以下的查询(我快速撰写了此帖子,但尚未进行测试,这只是要尝试的事情,因为我看到您的查询和发布的最高答案使用JOIN ON space op = 1,而不会使用空间索引):

SELECT   
     (SELECT p2.polygon_id
      FROM   T_Polygon p2
      WHERE  p2.coords.STIntersects(t.coords) = 1),
     t.pin_id
FROM     T_PIN t
WHERE    
     (SELECT t.coords.STIntersects(p.coords)
      FROM   T_POLYGON p
      WHERE  t.coords.STIntersects(p.SimplePolysGeog) = 1) = 1

仅供参考:如果SimplePolysGeog最终重叠,则上述方法不起作用(例如,可以在两个简化的geog中使用大头针,只是在状态下对辖区中的人运行此操作,并且由于正常的多边形共享边界,所以边界框重叠了),因此在大多数情况下使用情况下,将引发错误,即子查询返回多个结果。

来自MS Docs的空间索引概述

空间索引支持的地理方法

在某些条件下,空间索引支持以下面向集合的地理方法:STIntersects(),STEquals()和STDistance()。为了得到空间索引的支持,这些方法必须在查询的WHERE子句中使用,并且必须在以下一般形式的谓词中出现:

geography1.method_name(geography2)comparison_operatorvalid_number

要返回非空结果,geography1geography2必须具有相同的空间参考标识符(SRID)。否则,该方法返回NULL。

空间索引支持以下谓词形式:


使用空间索引的查询

仅在WHERE子句中包含索引空间运算符的查询中才支持空间索引。例如语法,例如:

[spatial object].SpatialMethod([reference spatial object]) [ = | < ] [const literal or variable]

查询优化器了解空间运算(@a.STIntersects(@b) = @b.STInterestcs(@a))的可交换性。但是,如果比较的开始不包含空间运算符(例如,WHERE 1 = spatial op将不使用空间索引),则不会使用空间索引。要使用空间索引,请重写比较(例如WHERE spatial op = 1)。

...

如果SimplePolysGeogs重叠,以下查询将起作用:

;WITH cte AS
(
   SELECT T_PIN.PIN_ID, 
          T_POLYGON.POLYGON_ID, 
          T_POLYGON.COORD 
   FROM T_PIN 
   INNER JOIN T_POLYGON
   ON T_PIN.COORD.STIntersects(T_POLYGON.SimplePolysGeog) = 1
)

SELECT COUNT(*)
FROM T_PIN 
INNER JOIN cte
ON T_PIN_PIN_ID = cte.PIN_ID
where cte.[COORD].STIntersects(T_PIN.COORD) = 1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.