索引列上的Oracle EBS表达式可防止使用索引

时间:2018-04-26 14:38:40

标签: oracle view oracle11g sql-tuning

Oracle数据库11.1.0.7和EBS应用程序11.5.10。

我的自定义视图效果不佳,其中包括Oracle EBS基本视图APPS.RA_CUSTOMER_TRX。该基本视图针对基表RA_CUSTOMER_TRX_ALL,如下所示。

FROM RA_CUSTOMER_TRX_ALL
     WHERE NVL (
               ORG_ID,
               NVL (
                   TO_NUMBER (
                       DECODE (SUBSTRB (USERENV ('CLIENT_INFO'), 1, 1),
                               ' ', NULL,
                               SUBSTRB (USERENV ('CLIENT_INFO'), 1, 10))),
                   -99)) =
           NVL (
               TO_NUMBER (
                   DECODE (SUBSTRB (USERENV ('CLIENT_INFO'), 1, 1),
                           ' ', NULL,
                           SUBSTRB (USERENV ('CLIENT_INFO'), 1, 10))),
               -99);

当我从Toad运行Oracle Tuning Advisor反对我的自定义视图的sql时,它告诉我

1- Restructure SQL finding (see plan 1 in explain plans section)
----------------------------------------------------------------
  The predicate NVL("RA_CUSTOMER_TRX_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB
  (USERENV('CLIENT_INFO'),1,1),' ',NULL,SUBSTRB(USERENV('CLIENT_INFO'),1,10))),
  (-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(USERENV('CLIENT_INFO'),1,1),'
  ',NULL,SUBSTRB(USERENV('CLIENT_INFO'),1,10))),(-99)) used at line ID 53 of
  the execution plan contains an expression on indexed column "ORG_ID". This 
  expression prevents the optimizer from efficiently using indices on table
  "AR"."RA_CUSTOMER_TRX_ALL".

  Recommendation
  --------------
  - Rewrite the predicate into an equivalent form to take advantage of
    indices. Alternatively, create a function-based index on the expression.

  Rationale
  ---------
    The optimizer is unable to use an index if the predicate is an inequality
    condition or if there is an expression or an implicit data type conversion
    on the indexed column.

所以我的选择是1)重写自定义视图以反对基表而不是基本视图,或2)创建基于函数的索引。我尝试创建基于函数的索引,但它告诉我"只能将纯函数编入索引。"

我想通过创建索引来解决这个问题,因为我相信其他类似的观点会从中受益。我的DBA技能不够高,所以希望有人之前遇到过这个并且可以提出建议。

谢谢。

1 个答案:

答案 0 :(得分:0)

NVL不会短路,因此您的整个表达式总是被完整评估,包括对USERENV的三次调用。我还不清楚你的CLIENT_INFO字符串的格式是什么样的。我假设它是一个数字或空白。我还没有完全解决这个问题,但是你可以将where子句更改为更像:

WHERE ( org_id IS NOT NULL AND USERENV ('CLIENT_INFO') IS NOT NULL AND
        org_id = TO_NUMBER( USERENV ('CLIENT_INFO')))
   OR ( org_id IS NOT NULL AND USERENV ('CLIENT_INFO') IS NULL AND org_id = -99 )
   OR ( org_id IS NULL AND USERENV ('CLIENT_INFO') IS NULL )

您还可以尝试使用公用表格表达式缓存对USERENV的调用,但我不确定这会有所帮助:

WITH clientinfo AS (
    SELECT /*+ MATERIALIZE */ USERENV( 'CLIENT_INFO' ) AS client_info FROM dual
)
SELECT ...
  FROM ...
 INNER JOIN clientinfo ci
 WHERE ci.client_info = ...