对于Geom Field Recalc是WHERE还是INNER JOIN首选?

时间:2014-10-29 14:49:18

标签: postgresql postgis

我已经阅读了很多帖子,讨论了使用INNER JOIN和WHERE的优点。我发现的解释似乎都表明INNER JOIN的偏好更多地与可读性相关而不是功能性。但是,在应用这两种方法重新计算几何字段时,我在计算时间方面遇到了巨大的差异。

在下面的示例中,point_geoms.point_within字段是相同类型的几何类型字段,SRID为“POINTS”.geom

例如,这个WHERE版本几乎立即在~5k记录的子集上运行:

UPDATE "POINTS"
SET geom = point_geoms.point_within
FROM point_geoms
WHERE "POINTS"."POINT_ID" = point_geoms.point_id

虽然这个INNER JOIN版本会继续旋转,即使被要求只计算一行:

UPDATE "POINTS"
SET geom = p.point_within
FROM "POINTS" s
INNER JOIN point_geoms p
ON s."POINT_ID" = p.point_id

当我基于相同的INNER JOIN进行SELECT时,它会相当快地返回子集中的所有〜5k记录...这让我更加困惑INNER JOIN更新为什么只是一直旋转。当我解析选择版本时,看起来它只扫描point_geoms表中的~5k行,但是当更新它时,看起来它想要扫描“POINTS”表中的所有〜1M +。

任何想法/解释都将不胜感激。也许我刚刚在这里做了些蠢事。无论哪种方式,都非常想了解最新情况。提前谢谢!

1 个答案:

答案 0 :(得分:0)

由于UPDATE的工作方式,INNER JOIN版本包含"POINTS"及其别名s之间不受限制的自我加入,所以有很多(!)更多行更新。更糟糕的是,每个目标行有多个输出行需要更新,因此,如果您等到UPDATE完成后,您将获得不确定的结果。

UPDATE FROM条款与SELECT查询的条款完全相同,因为UPDATE&#39}目标表("POINTS")包含在内而没有明确提及。见the PostgreSQL Docs,其中说:

  

当存在FROM子句时,实质上发生的是目标表连接到from_list中提到的表,并且连接的每个输出行表示目标表的更新操作。使用FROM时,应确保连接为每个要修改的行生成最多一个输出行。换句话说,目标行不应该与其他表连接到多个行。如果是,那么只有一个连接行将用于更新目标行,但是将使用哪一个不容易预测。

     

由于这种不确定性,仅在子选择内引用其他表更安全,但通常比使用连接更难阅读和更慢。

当第二个查询引入FROM "POINTS" s行时,它会有效地从CROSS JOIN创建"POINTS"到自身。请注意EXPLAIN输出如何包含2个序列扫描:一个用于"POINTS",另一个用于其别名"POINTS" s

postgres=# EXPLAIN UPDATE "POINTS"
SET geom = p.point_within
FROM "POINTS" s
INNER JOIN point_geoms p
ON s."POINT_ID" = p.point_id;
                                           QUERY PLAN                                           
------------------------------------------------------------------------------------------------
 Update on "POINTS"  (cost=140.50..62539765.00 rows=5000000000 width=28)
   ->  Nested Loop  (cost=140.50..62539765.00 rows=5000000000 width=28)
         ->  Seq Scan on "POINTS"  (cost=0.00..15406.00 rows=1000000 width=10)
                                    ... snip ...
                     ->  Seq Scan on "POINTS" s  (cost=0.00..15406.00 rows=1000000 width=10)

此更新的输出行数为5,000,000,000,如果更新完成,更新后的值将是不确定的,即垃圾。

另一个版本完全不同,只生成5,000个输出行 这个过程。它不仅更快,而且可能是预期的。

据我所知,在制作INNER JOIN时,无法直观地使用UPDATE语法。我希望这可以帮助您了解正在发生的事情。