在 Oracle XE 11g 中按不使用索引的两列排序
Order by two columns not using index in Oracle XE 11g
为什么这个用于检索前 100 行的简单查询,从给定时间开始,按时间和主键排序(client_time
不是唯一的,这就是按两者排序的原因),不使用索引?
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')
我怀疑原因是 WHERE
子句:
WHERE client_time >= TO_TIMESTAMP('2017-07-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS')
您指定 client_time
是 TIMESTAMP WITH TIMEZONE
。但是,常量只是 TIMESTAMP
,没有时区。这意味着需要转换类型——这通常会阻碍索引的使用。
您应该尝试使用 TO_TIMESTAMP_TZ()
,记录在案 here。
问题是我的索引建立在 VARCHAR2
列上。由于NLS,索引顺序不能直接用来对结果集进行排序。
将 transaction_id 更改为 NUMBER
解决了问题。
为什么这个用于检索前 100 行的简单查询,从给定时间开始,按时间和主键排序(client_time
不是唯一的,这就是按两者排序的原因),不使用索引?
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')
我怀疑原因是 WHERE
子句:
WHERE client_time >= TO_TIMESTAMP('2017-07-01 10:00:00', 'YYYY-MM-DD HH24:MI:SS')
您指定 client_time
是 TIMESTAMP WITH TIMEZONE
。但是,常量只是 TIMESTAMP
,没有时区。这意味着需要转换类型——这通常会阻碍索引的使用。
您应该尝试使用 TO_TIMESTAMP_TZ()
,记录在案 here。
问题是我的索引建立在 VARCHAR2
列上。由于NLS,索引顺序不能直接用来对结果集进行排序。
将 transaction_id 更改为 NUMBER
解决了问题。