在数字表上交叉连接以获取线顶点,有没有更好的方法?

Wil*_*son 8 oracle spatial geometry number-table

问题:

我有一个空间表(道路线),使用 ESRI 的SDE.ST_GEOMETRY用户定义数据类型存储在 Oracle 12c地理数据库中。我想列出线顶点,以便我最终可以访问和更新它们的坐标。如果我使用的是 SDO_GEOMETRY/Oracle Locator,那么我会使用该 SDO_UTIL.GETVERTICES函数。但是我没有使用 SDO_GEOMETRY/Oracle Locator,并且SDE.ST_GEOMETRY. 唯一的SDE.ST_GEOMETRY 功能,我可以找到属于顶点ST_PointNST_NumPoints

我想出了一个成功完成所有这些的查询 - 将线顶点作为行(受此页面启发):

1    SELECT   a.ROAD_ID
2             ,b.NUMBERS VERTEX_INDEX
3             ,a.SDE.ST_X(SDE.ST_PointN(a.SHAPE, b.NUMBERS)) AS X
4             ,a.SDE.ST_Y(SDE.ST_PointN(a.SHAPE, b.NUMBERS)) AS Y
5    FROM     ENG.ROADS a
6             CROSS JOIN ENG.NUMBERS b
7    WHERE    b.NUMBERS <= SDE.ST_NumPoints(a.SHAPE)
8    --removed to do explain plan: ORDER BY ROAD_ID, b.NUMBERS

----------------------------------------------------------------------------------------------------
| Id  | Operation           | Name                 | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |                      |  5996 |  1545K|       |   262   (1)| 00:00:01 |
|   1 |  MERGE JOIN         |                      |  5996 |  1545K|       |   262   (1)| 00:00:01 |
|   2 |   INDEX FULL SCAN   | R23715_SDE_ROWID_UK  |    30 |    90 |       |     1   (0)| 00:00:01 |
|*  3 |   SORT JOIN         |                      |  3997 |  1018K|  2392K|   261   (1)| 00:00:01 |
|   4 |    TABLE ACCESS FULL| ROAD                 |  3997 |  1018K|       |    34   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
"   3 - access(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
"       filter(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
Run Code Online (Sandbox Code Playgroud)

它将表中CROSS JOINS的行ROADS转换为NUMBERS表(并将结果限制为每行中的顶点数)。

统计数据:(更新)

  • 每条线最多有 30 个顶点(平均每条线 4.38 个顶点)
  • ROADS 有 3,997 条线路
  • NUMBERS 有 30 行(从 1 开始的连续数字)
  • 结果集有 17,536 行

但是,性能很差(40 秒),我不禁思考 - 有没有更优雅的方法来做到这一点?对我来说,使用数字表和交叉连接似乎是一种草率的方法。有没有更好的办法?

外行的条款将不胜感激;我是一名公共工程人员,而不是 DBA。


更新 #1:

如果我从查询中删除第 3 行和第 4 行(X 和 Y 相关函数的字符串),它会立即执行。但当然,我不能只删除这些行,我需要X 和 Y 列。所以这让我相信性能缓慢与 X 和 Y 函数有关。

但是,如果我将点导出到静态表,然后在其上运行 X 和 Y 函数,这也会立即执行。

那么,这是否意味着性能缓慢是由 X 和 Y 函数引起的,除了,好吧,不是吗?我糊涂了。


更新#2:

如果我将 X 和 Y 从查询中取出,将它们放在外部查询中,并将 ROWNUM 添加到内部查询中,那么它会快得多(16 秒 - 更新):

    SELECT
        ROWNUM
        ,ROAD_ID
        ,VERTEX_INDEX
        ,SDE.ST_X(ST_POINT) AS X
        ,SDE.ST_Y(ST_POINT) AS Y
    FROM
    (
        SELECT  
              ROWNUM
              ,a.ROAD_ID
              ,b.NUMBERS VERTEX_INDEX
              ,SDE.ST_PointN(a.SHAPE, b.NUMBERS) AS ST_POINT
        FROM  ENG.ROAD a
              CROSS JOIN ENG.NUMBERS b
        WHERE b.NUMBERS <= SDE.ST_NumPoints(a.SHAPE)
    )
    --removed to do explain plan: ORDER BY ROAD_ID, VERTEX_INDEX

-------------------------------------------------------------------------------------------------------
| Id  | Operation              | Name                 | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT       |                      |  5996 |   322K|       |   262   (1)| 00:00:01 |
|   1 |  COUNT                 |                      |       |       |       |            |          |
|   2 |   VIEW                 |                      |  5996 |   322K|       |   262   (1)| 00:00:01 |
|   3 |    COUNT               |                      |       |       |       |            |          |
|   4 |     MERGE JOIN         |                      |  5996 |  1545K|       |   262   (1)| 00:00:01 |
|   5 |      INDEX FULL SCAN   | R23715_SDE_ROWID_UK  |    30 |    90 |       |     1   (0)| 00:00:01 |
|*  6 |      SORT JOIN         |                      |  3997 |  1018K|  2392K|   261   (1)| 00:00:01 |
|   7 |       TABLE ACCESS FULL| ROAD                 |  3997 |  1018K|       |    34   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------
"   6 - access(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
"       filter(""B"".""NUMBERS""<=""SDE"".""ST_NumPoints""(""A"".""SHAPE""))"
Run Code Online (Sandbox Code Playgroud)

Justin Cave 解释了为什么 ROWNUM 有助于提高性能:为什么将 ROWNUM 添加到查询可以提高性能?

虽然这种性能改进很好,但还不够好。我不禁想到我仍然没有完全理解查询是如何工作的,或者它为什么这么慢。

问题仍然存在:有没有更好的方法?

Joe*_*ish 7

我对 Oracle 性能有所了解,但对自定义数据类型几乎一无所知,但我会尽力为您提供一个提高性能的计划。

1) 确认您无法获得解释计划。

即使您没有复杂的数据库软件,也可以获得解释计划。如果执行会发生什么set autotrace on explain

您也可以尝试DBMS_XPLAN。首先通过使用一些额外的关键字包装您的查询来节省计划:

explain plan for (SELECT... your query goes here); 
Run Code Online (Sandbox Code Playgroud)

然后执行这个:

SELECT PLAN_TABLE_OUTPUT FROM TABLE(DBMS_XPLAN.DISPLAY());

有可能这两种方法都行不通,您真的无法获得解释计划。我只是想验证一下,因为有了解释计划,社区会更容易为您提供帮助。

2) 考虑需求。

你说20秒不够好。您或其他人是否准确定义了什么是足够好?有没有商量的余地?您的查询是否需要恰好是一个 SELECT 查询?您能否一步填充全局临时表并在下一步中选择您想要的结果?你能创建一个返回结果集存储过程并调用它吗?

3) 为完成查询所需的时间建立一个下限。

我建议运行一个“作弊”的简单查询,以找出优化良好的查询会是什么样子。例如,这个只获取第一个顶点的查询需要多长时间?

SELECT
    ROWNUM
    ,ROAD_ID
    ,VERTEX_INDEX
    ,SDE.ST_X(ST_POINT) AS X
    ,SDE.ST_Y(ST_POINT) AS Y
FROM
(
    SELECT  
          ROWNUM
          ,a.ROAD_ID
          ,1 VERTEX_INDEX
          ,SDE.ST_PointN(a.SHAPE, 1) AS ST_POINT
    FROM  ENG.ROAD a
)
ORDER BY ROAD_ID, VERTEX_INDEX;
Run Code Online (Sandbox Code Playgroud)

我怀疑这会给你 4000 行。如果将该查询的响应时间乘以 17.5/4,则可以为总执行时间提供一个很好的下限。

如果您的总执行时间下限比您在第 2 步中建立的下限长,那么您要么需要通过提前计算结果并将其存储在表格中来创造性地使用数据模型,要么需要重新协商所需的响应时间。

4) 基准以确定哪些函数对您的执行时间贡献最大。

您在更新 #1 上走在正确的轨道上,但您需要尝试控制正在完成的工作量。例如,是否可以编写一组相对简单的查询,使每个函数精确执行 10000 次?响应时间如何比较?

5)去上班。

根据步骤 2 中建立的要求以及您在步骤 4 中发现的内容,尝试您能想到的任何技巧来减少查询运行时间。您是否能够预先计算结果并保存它们?如果问题与函数执行的次数有关,则未记录的物化提示可能会有所帮助。这迫使 Oracle 在幕后创建一个隐藏的临时表来存储结果。我不知道它是否与您使用的特殊数据类型兼容。

例如,也许这样的东西表现更好?抱歉,如果它不能编译但我无法测试。

WITH ROAD_CTE (ROAD_ID, VERTEX_INDEX, SHAPE) AS
(
    SELECT /*+ materalize */
      a.ROAD_ID
    , b.NUMBERS VERTEX_INDEX
    , a.SHAPE
    FROM ENG.ROAD a
    CROSS JOIN ENG.NUMBERS b
    WHERE b.NUMBERS <= SDE.ST_NUMPOINTS(a.SHAPE)
)
, CTE_WITH_ST_POINT (ROAD_ID, VERTEX_INDEX, ST_POINT) AS
(
    SELECT /*+ materalize */
      rcte.ROAD_ID
    , rcte.VERTEX_INDEX
    , SDE.ST_PointN(rcte.SHAPE, rcte.VERTEX_INDEX) ST_POINT
    FROM ROAD_CTE rcte
)
SELECT 
      ROAD_ID
    , VERTEX_INDEX
    , SDE.ST_X(ST_POINT) AS X
    , SDE.ST_Y(ST_POINT) AS Y
FROM CTE_WITH_ST_POINT
ORDER BY ROAD_ID, VERTEX_INDEX;
Run Code Online (Sandbox Code Playgroud)

如果你在这一切之后仍然被困住,我怀疑它至少会给你额外的信息,你可以编辑到问题中。祝你好运!