我有下表:
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。
SDO_RELATE
查询规划器的行为SDO_RELATE
只会使用第一个参数的空间索引。该文档并没有明确说明这一点,但可以从那里的一些细节中收集到。两个几何参数的文档说:
(强调我的)
这非常强烈地表明
SDO_RELATE
它专门设计用于在其第一个参数上使用空间索引。MD1
但是,由于您的子句中有一个主键过滤器 onWHERE
,因此规划器正确地得出结论,使用主键索引会比使用空间索引更快MD1
。它从不考虑反转函数的参数,并且由于SDO_RELATE
在第一个参数而不是第二个参数上使用索引,所以它从不考虑实际使用空间索引。SDO_FILTER
据我所知,行为方式相同。解决方案
答案很简单,也很不直观。只需将参数的顺序切换为
SDO_RELATE
:这导致它现在使用空间索引:
查询现在几乎是即时的,运行时间约为 50 毫秒。
对于这种情况,请忽略
ORDERED
文档中的提示部分该文档稍后继续说:
由此,我们可能认为我们也可以通过将
MD2
first 放在列表中并指定ORDERED
提示来强制规划器使用索引:这给了我们一个不同的执行计划:
这确实会强制使用索引,但它的使用方式是错误的。它仍在对 进行全表扫描
MD2
。MD2
它不是通过 的结果集中的单独几何过滤,而是从中MD1
获取表中的几乎所有行MD2
(注意计划第 2 行的 148K 计数),执行两次搜索MD1
(一次通过主键,另一次通过比较使用空间索引的所有行MD2
)和位图与结果。这实际上会使性能变差,对空间索引进行完全不必要的扫描,并将该扫描与主键扫描进行比较。作为一般规则,您希望首先将具有最小几何图形集的表(在其他过滤器之后)放在您的
FROM
子句中,并让它使用第二个表上的空间索引。仅
ORDERED
在原始查询中指定提示不会改变原始计划。