WHERE子句中的Oracle标量函数导致性能不佳

时间:2012-08-09 15:07:15

标签: oracle oracle10g

我编写了一个标量函数(DYNAMIC_DATE),用于将文本值转换为日期/时间。例如,DYANMIC_DATE('T-1')(T-1 =今天减去1 ='昨天')返回08-AUG-2012 00:00:00。它还接受日期字符串:DYNAMIC_DATE('10/10/1974')

该函数使用CASE语句来解析唯一参数并计算相对于sysdate的日期。

虽然它没有在其架构中使用任何表,但它确实使用TABLE类型来存储日期格式字符串:

TYPE VARCHAR_TABLE IS TABLE OF VARCHAR2(10);
formats VARCHAR_TABLE := VARCHAR_TABLE ('mm/dd/rrrr','mm-dd-rrrr','rrrr/mm/dd','rrrr-mm-dd');

当我在SELECT子句中使用该函数时,查询将返回< 1秒:

SELECT  DYNAMIC_DATE('MB-1') START_DATE, DYNAMIC_DATE('ME-1') END_DATE
FROM    DUAL

如果我在日期维度表(91311总记录)中使用它,则查询在< 1秒:

SELECT  count(1)
from    date_dimension
where   calendar_dt between DYNAMIC_DATE('MB-1') and  DYNAMIC_DATE('ME-1')

然而,如果对较大的表(26,301,317条记录)使用该函数,则其他函数存在问题:

/*
cost: 148,840
records: 151,885
time: ~20 minutes
*/
SELECT  count(1)
FROM    ORDERS ord
WHERE   trunc(ord.ordering_date) between DYNAMIC_DATE('mb-1') and DYNAMIC_DATE('me-1')

然而,使用“硬编码”日期的相同查询返回相当快:

/*
cost: 144,257
records: 151,885
time: 62 seconds
*/
SELECT  count(1)
FROM    ORDERS ord
WHERE   trunc(ord.ordering_date) between to_date('01-JUL-2012','dd-mon-yyyy') AND to_date('31-JUL-2012','dd-mon-yyyy')

供应商的vanilla安装不包括ORDERING_DATE字段的索引。

两个查询的解释计划类似:

功能: using function

具有硬编码日期: hard-coded dates

  • DYNAMIC_DATE子句中是否重复调用WHERE函数?
  • 还有什么可以解释这种差异?

**编辑**

在ORDERS表中添加了NONUNIQUE索引。两个查询都在< 1秒。两个计划都是相同的(方法),但具有该功能的计划是较低的成本。

我从该函数中删除了DETERMINISTIC关键字;在<中执行的查询1秒。

  • 问题是真的与函数有关还是与表有关?
  • 3年后,当此表格更大时,如果我不包含DETERMINISTIC关键字,查询效果会受到影响吗?
  • DETERMINISTIC关键字对函数的结果有影响吗?如果我明天运行DYNAMIC_DATE('T-1'),我会得到与今天(2012年9月8日)运行时相同的结果吗?如果是这样,这种方法将无效。

2 个答案:

答案 0 :(得分:1)

如果计划的步骤相同,那么完成的工作总量应该相同。如果您跟踪会话(像SQL * Plus中的set autotrace on那样简单,或者像事件10046跟踪那样更复杂),或者如果您看DBA_HIST_SQLSTAT假设您拥有对AWR表的许可访问权限,那么您是否(大致)看到两个查询的逻辑I / O和CPU消耗量相同?您运行的运行时差异是否可能是运行第二个查询时缓存数据的结果?

答案 1 :(得分:1)

我猜这个问题与你的功能无关。尝试在trunc(ord.ordering_date)上创建基于函数的索引并查看解释计划。

 CREATE INDEX ord_date_index ON ord(trunc(ord.ordering_date));