在WHERE子句中的参数上使用DECODE会导致索引短路吗?

Rad*_*hiu 2 oracle performance stored-procedures oracle11g

我有一个带有多个必需参数的存储过程,并且SELECT其中有一条语句在其WHERE子句中具有多个条件,如下所示:

SELECT *
FROM TABLE
WHERE column_1 = param_1
   AND column_2 = param_2
   AND column_3 = param_3;
Run Code Online (Sandbox Code Playgroud)

此查询工作正常,并且可以正确使用表上的索引。但是需求的变化意味着需要对过程进行调整,以便您可以传递较少的参数(也许只是前两个),但是我们希望过程对存储过程的更改最少。

我提出的建议之一是使用一个DECODE函数来处理每个可能的NULL参数,如下所示:

SELECT *
FROM TABLE
WHERE column_1 = param_1
   AND column_2 = param_2
   AND column_3 = DECODE(param_3, null, column_3);
Run Code Online (Sandbox Code Playgroud)

这样,我认为由于该函数未应用于表列,因此仍将使用索引。我进行了一些测试,即使在这种情况下,查询仍然可以使用并使用索引。

但是我仍然与我们的架构师矛盾(没有其他解释),因为我在WHERE子句中使用函数,因此查询将不使用索引。

我不确定我的更改是否足以证明它将始终使用索引,还是不确定是否应该检查其他情况以及由于该功能无法使用索引的情况DECODE

任何帮助/建议/信息将不胜感激。

Lal*_*r B 5

你是对的。测试并证明。

设定

SQL> CREATE TABLE t AS SELECT LEVEL id FROM dual CONNECT BY LEVEL <=10;

Table created.

SQL>
SQL> CREATE INDEX id_indx ON t(ID);

Index created.
Run Code Online (Sandbox Code Playgroud)

测试用例

普通查询,没有任何功能:

SQL> set autot on explain
SQL>
SQL> SELECT * FROM t  WHERE ID = 5;

        ID
----------
         5


Execution Plan
----------------------------------------------------------
Plan hash value: 1629656632

----------------------------------------------------------------------------
| Id  | Operation        | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT |         |     1 |     3 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| ID_INDX |     1 |     3 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("ID"=5)
Run Code Online (Sandbox Code Playgroud)

在值(不在列)上使用DECODE

SQL> SELECT * FROM t  WHERE ID = decode(5, NULL, 3, 5);

        ID
----------
         5


Execution Plan
----------------------------------------------------------
Plan hash value: 1629656632

----------------------------------------------------------------------------
| Id  | Operation        | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT |         |     1 |     3 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| ID_INDX |     1 |     3 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("ID"=5)
Run Code Online (Sandbox Code Playgroud)

在值上使用NVL不在列上):

SQL> SELECT * FROM t  WHERE ID = nvl(5, 3);

        ID
----------
         5


Execution Plan
----------------------------------------------------------
Plan hash value: 1629656632

----------------------------------------------------------------------------
| Id  | Operation        | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT |         |     1 |     3 |     1   (0)| 00:00:01 |
|*  1 |  INDEX RANGE SCAN| ID_INDX |     1 |     3 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("ID"=5)
Run Code Online (Sandbox Code Playgroud)

在这三种情况中,首先使用索引

列上的DECODE:

SQL> SELECT * FROM t  WHERE decode(ID, NULL, 3, 5) = 5;

        ID
----------
         1
         2
         3
         4
         5
         6
         7
         8
         9
        10

10 rows selected.


Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |     1 |     3 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(DECODE(TO_CHAR("ID"),NULL,3,5)=5)
Run Code Online (Sandbox Code Playgroud)

列上的NVL:

SQL> SELECT * FROM t  WHERE nvl(ID, 3) = 3;

        ID
----------
         3


Execution Plan
----------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     3 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |     1 |     3 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter(NVL("ID",3)=3)

SQL>
Run Code Online (Sandbox Code Playgroud)

不出所料,由于您要在具有常规索引的列上应用函数,因此不使用索引。您需要一个基于函数的索引。

因此,您是对的,当您不在列上应用函数而在参数值上应用函数时,不必担心索引的使用。