【问题标题】:Order by two columns not using index in Oracle XE 11g在 Oracle XE 11g 中按不使用索引的两列排序
【发布时间】:2018-01-30 14:26:57
【问题描述】:

为什么这个从给定时间开始检索前 100 行的简单查询不使用索引?

SELECT *
FROM (SELECT *
      FROM requests
      WHERE client_time >= TO_TIMESTAMP('2017-07-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS')
      ORDER BY client_time ASC, transaction_id ASC
     )
WHERE rownum <= 100;

client_time 是TIMESTAMP WITH LOCAL TIME ZONE,transaction_id 是VARCHAR2(255 CHAR)

我希望它使用的索引被定义为

CREATE UNIQUE INDEX idx_time_id REQUESTS (client_time, transaction_id);

查询执行需要大约 2 秒(我的系统中有 600 万行,生产中会更多)并产生以下计划:

----------------------------------------------------------------------------------------------------
| Id  | Operation               | Name             | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT        |                  |   100 |   110K|       | 31237   (1)| 00:06:15 |
|*  1 |  COUNT STOPKEY          |                  |       |       |       |            |          |
|   2 |   VIEW                  |                  |   860K|   931M|       | 31237   (1)| 00:06:15 |
|*  3 |    SORT ORDER BY STOPKEY|                  |   860K|    65M|    86M| 31237   (1)| 00:06:15 |
|*  4 |     TABLE ACCESS FULL   | REQUESTS         |   860K|    65M|       | 15294   (1)| 00:03:04 |
----------------------------------------------------------------------------------------------------

谓词信息(由操作id标识):

   1 - filter(ROWNUM<=100)
   3 - filter(ROWNUM<=100)
   4 - filter("CLIENT_TIME">=TIMESTAMP' 2017-07-01 10:00:00,000000000')

当我删除我的 ORDER BY 子句的第二部分时,实际上使用了这个索引并且查询在大约 1 毫秒内执行。

如果我得到this Use the index, Luke 的文章是正确的,我的查询不应该也使用这个索引吗?

更新:

删除我的二阶列后的计划如下:

--------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                  |   100 | 65100 |   106   (0)| 00:00:02 |
|*  1 |  COUNT STOPKEY                |                  |       |       |            |          |
|   2 |   VIEW                        |                  |   102 | 66402 |   106   (0)| 00:00:02 |
|   3 |    TABLE ACCESS BY INDEX ROWID| TRX_REQUESTS_LTZ |   102 |  8160 |   106   (0)| 00:00:02 |
|*  4 |     INDEX RANGE SCAN          | IDX_TIME_ID      |       |       |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

所以我相信 WHERE 子句不是这里的问题。此外,像这样重写 WHERE 后没有任何变化:

WHERE client_time >= TO_TIMESTAMP_TZ('2017-07-01 10:00:00 +10:00', 'YYYY-MM-DD HH24:MI:SS TZH:TZM')

【问题讨论】:

    标签: sql indexing oracle11g oracle-xe


    【解决方案1】:

    我怀疑原因是WHERE 子句:

      WHERE client_time >= TO_TIMESTAMP('2017-07-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS')
    

    您已指定client_timeTIMESTAMP WITH TIMEZONE。但是,常量只是一个TIMESTAMP,没有时区。这意味着需要转换类型——这通常会阻碍索引的使用。

    您应该尝试使用TO_TIMESTAMP_TZ(),记录在案的here

    【讨论】:

    • 我order by的第二部分是问题,没有它,使用索引!我相信,您所说的情况是,当索引无法使用时,因为 oracle 隐式应用了转换函数。不是这种情况。不过我试过你的建议。像这个 client_time >= TO_TIMESTAMP_TZ('2017-07-01 10:00:00 +10:00', 'YYYY-MM-DD HH24:MI:SS TZH:TZM')。它没有任何区别。
    【解决方案2】:

    问题是我的索引是建立在VARCHAR2 列上的。由于 NLS,不能像这样使用索引顺序对结果集进行排序。

    将 transaction_id 更改为 NUMBER 解决了问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-18
      • 2018-07-07
      • 2023-03-17
      • 2016-04-26
      • 2021-11-08
      • 1970-01-01
      • 2013-11-29
      • 2017-12-30
      相关资源
      最近更新 更多