AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 181500
Accepted
jpmc26
jpmc26
Asked: 2017-07-22 20:23:35 +0800 CST2017-07-22 20:23:35 +0800 CST 2017-07-22 20:23:35 +0800 CST

为什么 Oracle 在 JOINing 表时不使用空间索引?(或者:为什么我在 Oracle 中使用空间 JOIN 的查询这么慢?)

  • 772

我有下表:

CREATE TABLE MY_DATA (
  MY_DATA_ID NUMBER(38,0) PRIMARY KEY,
  GEOM SDO_GEOMETRY
);

我已经设置了它的 SDO 元数据并创建了一个空间索引:

INSERT INTO USER_SDO_GEOM_METADATA (TABLE_NAME, COLUMN_NAME, SRID, DIMINFO)
VALUES ('MY_DATA', 'GEOM', 4326, MDSYS.SDO_DIM_ARRAY(MDSYS.SDO_DIM_ELEMENT('X',-180.0000,180.0000,0.00000000001),MDSYS.SDO_DIM_ELEMENT('Y',-90.0000,90,0.00000000001)));

CREATE INDEX SPIX_MY_DATA ON MY_DATA (GEOM) INDEXTYPE IS MDSYS.SPATIAL_INDEX;

该表有大约 150,000 行。

我需要找到几何与特定行相交的行。这似乎是一项足够简单的任务。只需要一个JOIN和一个WHERE子句:

SELECT /*+ INDEX(MD2 SPIX_MY_DATA)*/
    MD2.MY_DATA_ID
FROM MY_DATA MD1
JOIN MY_DATA MD2 ON SDO_RELATE(MD1.GEOM, MD2.GEOM, 'mask=ANYINTERACT') = 'TRUE'
WHERE MD1.MY_DATA_ID = 143668 AND MD2.MY_DATA_ID != 143668

但是这个查询非常慢。仅获取 8 个相关行需要超过 85 秒。我们可以从查询计划中看到它没有使用空间索引,尽管有索引提示:

Plan hash value: 4008712617

----------------------------------------------------------------------------------------------
| Id  | Operation                    | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |               |   379 |  9854 |  1390   (1)| 00:00:17 |
|   1 |  NESTED LOOPS                |               |   379 |  9854 |  1390   (1)| 00:00:17 |
|   2 |   TABLE ACCESS BY INDEX ROWID| MY_DATA       |     1 |    13 |     2   (0)| 00:00:01 |
|*  3 |    INDEX UNIQUE SCAN         | SYS_C00755898 |     1 |       |     1   (0)| 00:00:01 |
|*  4 |   TABLE ACCESS FULL          | MY_DATA       |   379 |  4927 |  1388   (1)| 00:00:17 |
----------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   3 - access("MD1"."MY_DATA_ID"=143668)
   4 - filter("MD2"."MY_DATA_ID"<>143668 AND 
              "MDSYS"."SDO_RTREE_RELATE"("MD1"."GEOM","MD2"."GEOM",'mask=ANYINTERACT 
              querytype=window  ')='TRUE')

(第 2 行是 MD1,第 4 行是 MD2。)

我尝试将提示更改为/*+ INDEX(MY_DATA SPIX_MY_DATA)*/,但这也没有效果。我已确保我的统计数据是最新的。

我怎样才能加快这个查询?我怎样才能让它使用空间索引?

我目前正在使用 Oracle 11.2。

oracle index
  • 1 1 个回答
  • 951 Views

1 个回答

  • Voted
  1. Best Answer
    jpmc26
    2017-07-22T20:23:35+08:002017-07-22T20:23:35+08:00

    SDO_RELATE查询规划器的行为

    SDO_RELATE只会使用第一个参数的空间索引。该文档并没有明确说明这一点,但可以从那里的一些细节中收集到。

    两个几何参数的文档说:

    geometry1:指定表中的几何列。该列必须具有空间索引。数据类型是 SDO_GEOMETRY。

    geometry2指定表中的几何图形或几何图形的瞬态实例。(使用绑定变量或 SDO_GEOMETRY 构造函数指定。)数据类型为 SDO_GEOMETRY。

    (强调我的)

    这非常强烈地表明SDO_RELATE它专门设计用于在其第一个参数上使用空间索引。MD1但是,由于您的子句中有一个主键过滤器 on WHERE,因此规划器正确地得出结论,使用主键索引会比使用空间索引更快MD1。它从不考虑反转函数的参数,并且由于SDO_RELATE在第一个参数而不是第二个参数上使用索引,所以它从不考虑实际使用空间索引。

    SDO_FILTER据我所知,行为方式相同。

    解决方案

    答案很简单,也很不直观。只需将参数的顺序切换为SDO_RELATE:

    SELECT
        MD2.MY_DATA_ID
    FROM MY_DATA MD1
    JOIN MY_DATA MD2 ON SDO_RELATE(MD2.GEOM, MD1.GEOM, 'mask=ANYINTERACT') = 'TRUE'
    WHERE MD1.MY_DATA_ID = 143668 AND MD2.MY_DATA_ID != 143668
    

    这导致它现在使用空间索引:

    Plan hash value: 3780744499
    
    ----------------------------------------------------------------------------------------------
    | Id  | Operation                    | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
    ----------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT             |               |  1487 | 38662 |   534   (1)| 00:00:07 |
    |   1 |  NESTED LOOPS                |               |  1487 | 38662 |   534   (1)| 00:00:07 |
    |   2 |   TABLE ACCESS BY INDEX ROWID| MY_DATA       |     1 |    13 |     2   (0)| 00:00:01 |
    |*  3 |    INDEX UNIQUE SCAN         | SYS_C00755898 |     1 |       |     1   (0)| 00:00:01 |
    |*  4 |   TABLE ACCESS BY INDEX ROWID| MY_DATA       |  1487 | 19331 |   534   (1)| 00:00:07 |
    |*  5 |    DOMAIN INDEX              | SPIX_MY_DATA  |       |       |     0   (0)| 00:00:01 |
    ----------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       3 - access("MD1"."MY_DATA_ID"=143668)
       4 - filter("MD2"."MY_DATA_ID"<>143668)
       5 - access("MDSYS"."SDO_RTREE_RELATE"("MD2"."GEOM","MD1"."GEOM",'mask=ANYINTERACT 
                  querytype=window  ')='TRUE')
    

    查询现在几乎是即时的,运行时间约为 50 毫秒。

    对于这种情况,请忽略ORDERED文档中的提示部分

    该文档稍后继续说:

    geometry2可以来自表或者是瞬态 SDO_GEOMETRY 对象,例如绑定变量或 SDO_GEOMETRY 构造函数。

    • 如果geometry2列没有空间索引,运算符在内存中索引查询窗口,性能非常好。

    • 如果将两个或多个几何图形 fromgeometry2传递给运算符,则必须指定 ORDERED 优化器提示,并且geometry2必须首先在 FROM 子句中指定表 in。

    由此,我们可能认为我们也可以通过将MD2first 放在列表中并指定ORDERED提示来强制规划器使用索引:

    SELECT /*+ ORDERED INDEX(MD2 SPIX_MY_DATA)*/
        MD2.MY_DATA_ID
    FROM MY_DATA MD2
    JOIN MY_DATA MD1 ON SDO_RELATE(MD1.GEOM, MD2.GEOM, 'mask=ANYINTERACT') = 'TRUE'
    WHERE MD1.MY_DATA_ID = 143668 AND MD2.MY_DATA_ID != 143668
    

    这给了我们一个不同的执行计划:

    Plan hash value: 3307523706
    
    --------------------------------------------------------------------------------------------------
    | Id  | Operation                        | Name          | Rows  | Bytes | Cost (%CPU)| Time     |
    --------------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT                 |               |  1487 | 38662 | 14806   (1)| 00:02:58 |
    |   1 |  NESTED LOOPS                    |               |  1487 | 38662 | 14806   (1)| 00:02:58 |
    |*  2 |   TABLE ACCESS FULL              | MY_DATA       |   148K|  1887K|  4752   (1)| 00:00:58 |
    |   3 |   BITMAP CONVERSION TO ROWIDS    |               |       |       |            |          |
    |   4 |    BITMAP AND                    |               |       |       |            |          |
    |   5 |     BITMAP CONVERSION FROM ROWIDS|               |       |       |            |          |
    |   6 |      SORT ORDER BY               |               |       |       |            |          |
    |*  7 |       DOMAIN INDEX               | SPIX_MY_DATA  |     1 |       |     0   (0)| 00:00:01 |
    |   8 |     BITMAP CONVERSION FROM ROWIDS|               |       |       |            |          |
    |*  9 |      INDEX RANGE SCAN            | SYS_C00755898 |     1 |       |     0   (0)| 00:00:01 |
    --------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       2 - filter("MD2"."MY_DATA_ID"<>143668)
       7 - access("MDSYS"."SDO_RTREE_RELATE"("MD1"."GEOM","MD2"."GEOM",'mask=ANYINTERACT 
                  querytype=window  ')='TRUE')
       9 - access("MD1"."MY_DATA_ID"=143668)
    

    这确实会强制使用索引,但它的使用方式是错误的。它仍在对 进行全表扫描MD2。MD2它不是通过 的结果集中的单独几何过滤,而是从中MD1获取表中的几乎所有行MD2(注意计划第 2 行的 148K 计数),执行两次搜索MD1(一次通过主键,另一次通过比较使用空间索引的所有行MD2)和位图与结果。这实际上会使性能变差,对空间索引进行完全不必要的扫描,并将该扫描与主键扫描进行比较。

    作为一般规则,您希望首先将具有最小几何图形集的表(在其他过滤器之后)放在您的FROM子句中,并让它使用第二个表上的空间索引。

    仅ORDERED在原始查询中指定提示不会改变原始计划。

    • 2

相关问题

  • 如何在数据库中找到最新的 SQL 语句?

  • 如何使用正则表达式查询名称?

  • 我在索引上放了多少“填充”?

  • RDBMS 上的“索引”是什么意思?[关闭]

  • 如何在 MySQL 中创建条件索引?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve