【问题标题】:Adding simple AND after JOIN kills performance在 JOIN 之后添加简单的 AND 会破坏性能
【发布时间】:2013-12-30 04:48:27
【问题描述】:

我有一个包含大约 500 个点的表格,我正在寻找公差范围内的重复项。这需要不到一秒钟的时间,并给了我 500 行。大多数距离为零,因为它给出了相同的点(PointA = PointB)

DECLARE @TOL AS REAL
SET @TOL = 0.05

SELECT 
    PointA.ObjectId as ObjectIDa,
    PointA.Name as PTNameA,
    PointA.[Description] as PTdescA,
    PointB.ObjectId as ObjectIDb,
    PointB.Name as PTNameB,
    PointB.[Description] as PTdescB,
    ROUND(PointA.Geometry.STDistance(PointB.Geometry),3) DIST
FROM CadData.Survey.SurveyPoint PointA
  JOIN [CadData].Survey.SurveyPoint PointB
    ON PointA.Geometry.STDistance(PointB.Geometry) < @TOL
   -- AND
   -- PointA.ObjectId <> PointB.ObjectID
ORDER BY ObjectIDa

如果我在底部附近使用注释掉的行,我会得到 14 行,但执行时间会增加到 14 秒。在我的积分表扩展到成千上万之前,这没什么大不了的。

如果答案已经存在,我提前道歉。我确实看过,但作为新手,我会迷失阅读那些让我头疼的帖子。

ADDENDUM: ObjectID 是一个 bigint 和 table 的 PK,所以我意识到我可以将语句更改为

AND PointA.ObjectID > PointB.ObjectID

现在这需要一半的时间并给我一半的结果(7 秒内 7 行)。我现在没有重复(因为第 4 点接近第 8 点,然后第 8 点接近第 4 点)。但是性能仍然让我担心,因为表会非常大,所以任何性能问题都会成为问题。

附录 2:更改 JOIN 和 AND(或建议的 WHERE)的顺序也没有区别。

DECLARE @TOL AS REAL
SET @TOL = 0.05

SELECT 
    PointA.ObjectId as ObjectIDa,
    PointA.Name as PTNameA,
    PointA.[Description] as PTdescA,
    PointB.ObjectId as ObjectIDb,
    PointB.Name as PTNameB,
    PointB.[Description] as PTdescB,
    ROUND(PointA.Geometry.STDistance(PointB.Geometry),3) DIST
FROM CadData.Survey.SurveyPoint PointA
  JOIN [CadData].Survey.SurveyPoint PointB
    ON PointA.ObjectId < PointB.ObjectID
    WHERE
    PointA.Geometry.STDistance(PointB.Geometry) < @TOL
ORDER BY ObjectIDa

我发现我可以将 @Tol 值更改为返回超过 100 行且性能没有变化的大值,即使它需要很多计算,这很有趣。但随后添加一个简单的 A

【问题讨论】:

  • SurveyPoint 表上有索引吗?专栏SurveyPoint.ObjectID呢?
  • SurveyPoint.ObjectID 是 PK 并已编入索引。我在几何列上也有一个空间索引。
  • 你要加入同一张桌子吗?
  • 你关心PointA和PointB不一样但是距离是0吗?如果您检查 PointA.Geometry.STDistance(PointB.Geometry) > 0 会怎样?
  • 是的 - 同一张桌子。我们的想法是在同一个表中找到足够接近的点以被视为单个点。

标签: sql sql-server geospatial spatial-query


【解决方案1】:

这是一个有趣的问题。

通过将“”更改为“>”,您可以获得很大的性能提升并非不现实。

正如其他人所提到的,诀窍是充分利用您的索引。当然,通过使用“>”,您应该可以轻松地让服务器限制在您的 PK 的特定范围内 - 当您已经检查“向前”时,避免“向后”查看。

此改进将扩展 - 将在您添加行时有所帮助。但是你担心它无助于阻止工作的增加是对的。正如您正确思考的那样,只要您必须扫描更多的行,就会花费更长的时间。之所以如此,是因为我们总是想比较一切。

如果第一部分看起来不错,只是 TOL 检查,您是否考虑过完全拆分第二部分?

将第一部分更改为转储到临时表中

SELECT 
    PointA.ObjectId as ObjectIDa,
    PointA.Name as PTNameA,
    PointA.[Description] as PTdescA,
    PointB.ObjectId as ObjectIDb,
    PointB.Name as PTNameB,
    PointB.[Description] as PTdescB,
    ROUND(PointA.Geometry.STDistance(PointB.Geometry),3) DIST

into #AllDuplicatesWithRepeats

FROM CadData.Survey.SurveyPoint PointA
  JOIN [CadData].Survey.SurveyPoint PointB
    ON 
    PointA.Geometry.STDistance(PointB.Geometry) < @TOL
ORDER BY ObjectIDa

您可以编写跳过重复项的直接查询,如下所示。它并不特别,但相对于临时表中的那个小集合,它应该非常快速。

Select
    *
from    
    #AllDuplicatesWithRepeats d1
        left join #AllDuplicatesWithRepeats d2 on (
                        d1.objectIDa = d2.objectIDb
                        and
                        d1.objectIDb = d2.objectIDa
                        )
where
    d2.objectIDb is null

【讨论】:

  • 是的,这很有趣,并且您的解决方案有效,尽管我确实简化了第二个选择(无需加入)。另外,我猜您忘记删除第一个连接中的一个约束?这就是我最终得到的结果,它在 2 秒内执行。 (太长 - 我将粘贴原始问题)
  • ——我修好了。感谢您了解这一点,@Land Surveyor。
【解决方案2】:

当您添加ObjectID 比较时,执行计划可能正在幕后执行某些操作。检查执行计划以查看查询的两个不同版本是否是,例如,使用索引搜索与表扫描。如果是这样,请考虑尝试使用query hints。

作为一种解决方法,您始终可以使用子查询:

DECLARE @TOL AS REAL
SET @TOL = 0.05

SELECT 
    ObjectIDa,
    PTNameA,
    PTdescA,
    ObjectIDb,
    PTNameB,
    PTdescB,
    DIST
FROM
(
SELECT 
  PointA.ObjectId as ObjectIDa,
    PointA.Name as PTNameA,
    PointA.[Description] as PTdescA,
    PointB.ObjectId as ObjectIDb,
    PointB.Name as PTNameB,
    PointB.[Description] as PTdescB,
    ROUND(PointA.Geometry.STDistance(PointB.Geometry),3) DIST
FROM CadData.Survey.SurveyPoint PointA
  JOIN [CadData].Survey.SurveyPoint PointB
    ON PointA.Geometry.STDistance(PointB.Geometry) < @TOL
   -- AND
   -- PointA.ObjectId <> PointB.ObjectID
) Subquery
WHERE ObjectIDa <> ObjectIDb
ORDER BY ObjectIDa

【讨论】:

  • 谢谢。子查询对我来说是新的,但没有区别。我发现了如何查看估计的执行计划,并将花一些时间了解它 - 特别是使用 100% 的过滤器。我还是新手。
  • 我的猜测是更快的查询是利用空间索引,这听起来对这个查询有好处,因为目标是找到靠近的点。也许引入 ObjectId 条件会导致查询优化器更喜欢主键的索引,考虑到“不等于”条件,这不是很有用。您可能能够看到在每个查询的执行计划中正在寻找/扫描哪个索引,以确认是否正在发生这种情况。
【解决方案3】:

尝试在JOIN 和ORDER BY 子句之间使用带有WHERE 子句的PointA.ObjectId &lt;&gt; PointB.ObjectID。

像这样:

DECLARE @TOL AS REAL
SET @TOL = 0.05

SELECT 
    PointA.ObjectId as ObjectIDa,
    PointA.Name as PTNameA,
    PointA.[Description] as PTdescA,
    PointB.ObjectId as ObjectIDb,
    PointB.Name as PTNameB,
    PointB.[Description] as PTdescB,
    ROUND(PointA.Geometry.STDistance(PointB.Geometry),3) DIST
FROM CadData.Survey.SurveyPoint PointA
  JOIN [CadData].Survey.SurveyPoint PointB
    ON PointA.Geometry.STDistance(PointB.Geometry) < @TOL
WHERE PointA.ObjectId <> PointB.ObjectID
ORDER BY ObjectIDa

【讨论】:

  • 有趣的想法,但使用 WHERE 代替 AND 对性能没有影响。
【解决方案4】:

感谢@Mike_M,这里是编辑后的 ​​Select,它在 2 秒内运行。

SELECT 
    PointA.ObjectId as ObjectIDa,
    PointA.Name as PTNameA,
    PointA.[Description] as PTdescA,
    PointB.ObjectId as ObjectIDb,
    PointB.Name as PTNameB,
    PointB.[Description] as PTdescB,
    ROUND(PointA.Geometry.STDistance(PointB.Geometry),3) DIST

into #AllDuplicatesWithRepeats

FROM CadData.Survey.SurveyPoint PointA
  JOIN [CadData].Survey.SurveyPoint PointB
    ON PointA.Geometry.STDistance(PointB.Geometry) < @TOL  
ORDER BY ObjectIDa

Select
    *
from    
    #AllDuplicatesWithRepeats d1
Where
    d1.ObjectIDa < d1.ObjectIDb

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-10
    • 1970-01-01
    • 2018-11-28
    • 1970-01-01
    • 1970-01-01
    • 2016-01-20
    • 1970-01-01
    相关资源
    最近更新 更多