我正在 Oracle 数据库中运行以下查询。在表 ORDERS 上创建索引 trunc(create_date)。但是当我运行下面的查询时,它没有使用索引。
SELECT ITEM, MAX("Orders") AS OP_ORD, MAX("SHIP") AS SHIP
FROM ORDERS
WHERE trunc(create_date) in(
SELECT TRUNC(sysdate)-6-(select to_char(sysdate, 'd') from dual) FROM DUAL
union
SELECT TRUNC(sysdate)-5-(select to_char(sysdate, 'd') from dual) FROM DUAL
union
SELECT TRUNC(sysdate)-4-(select to_char(sysdate, 'd') from dual) FROM DUAL
union
SELECT TRUNC(sysdate)-3-(select to_char(sysdate, 'd') from dual) FROM DUAL
)
GROUP BY ITEM_ID
但是如果我从子查询中删除最后一个查询,如下所示,那么它将使用索引
SELECT ITEM, MAX("Orders") AS OP_ORD, MAX("SHIP") AS SHIP
FROM ORDERS
WHERE trunc(create_date) in(
SELECT TRUNC(sysdate)-6-(select to_char(sysdate, 'd') from dual) FROM DUAL
union
SELECT TRUNC(sysdate)-5-(select to_char(sysdate, 'd') from dual) FROM DUAL
union
SELECT TRUNC(sysdate)-4-(select to_char(sysdate, 'd') from dual) FROM DUAL
)
GROUP BY ITEM_ID
知道为什么这不使用索引吗?
假设(根据您的个人资料)您的
NLS_TERRITORY = 'India'
并且您尝试选择从上周星期一到上周星期三或星期四的行,那么您可以过滤结果,而无需在 TRUNC
列上使用 create_date
或使用 IN
,而是在范围内进行过滤:
SELECT ITEM_ID,
MAX("Orders") AS OP_ORD,
MAX("SHIP") AS SHIP
FROM ORDERS
WHERE create_date >= TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY
AND create_date < TRUNC(SYSDATE, 'IW') - INTERVAL '3' DAY
GROUP BY ITEM_ID
这将/可能使用基础
create_date
列上的索引,而不是基于函数的 TRUNC(create_date)
索引。
(注意:使用
TRUNC(SYSDATE, 'IW')
将始终给出最近星期一的午夜,即 ISO 周的开始,无论用户具有什么设置。TO_CHAR(SYSDATE, 'd')
将根据 的 NLS_TERRITORY
设置给出不同的结果执行查询的用户的会话 - 即,如果两个不同的用户的 TO_CHAR(SYSDATE, 'd')
会话设置不同,则使用 NLS_TERRITORY
对于完全相同的查询可以获得不同的结果。)
您同样可以将查询写为:
SELECT ITEM_ID,
MAX("Orders") AS OP_ORD,
MAX("SHIP") AS SHIP
FROM ORDERS
WHERE TRUNC(create_date) IN (
TRUNC(SYSDATE, 'IW') - INTERVAL '7' DAY,
TRUNC(SYSDATE, 'IW') - INTERVAL '6' DAY,
TRUNC(SYSDATE, 'IW') - INTERVAL '5' DAY,
TRUNC(SYSDATE, 'IW') - INTERVAL '4' DAY
)
GROUP BY ITEM_ID
知道为什么这不使用索引吗?
优化器将查看查询并确定一个执行计划,根据可用的统计数据和其他启发式方法,确定该执行计划是“最佳”。对于三天的数据,可以确定基于索引过滤然后检索包含其他列数据的相应行是最有效的。然而,在四天的时间里,它可能会确定,由于数据量的增加,使用索引的效率较低,而仅执行全表扫描并忽略索引实际上效率更高。