且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

[20120612]函数索引中使用substr函数.txt

更新时间:2022-09-01 11:19:49

1.建立测试环境
SQL> select * from v$version ;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE    11.2.0.1.0      Production
TNS for Linux: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production

create table t as select rownum id ,cast(dbms_random.string('x',16) as varchar2(16)) name,lpad('x',100) text from dual connect by level

SQL> create index i_t_name on t(name);
Index created.

SQL> explain plan for select * from t where name=:x;
Explained.

SQL> select * from table(dbms_xplan.display(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS cost'));
PLAN_TABLE_OUTPUT
---------------------------
Plan hash value: 1766912905
----------------------------------------------------------------------
| Id  | Operation                   | Name     | E-Rows | Cost (%CPU)|
----------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |    115 |    41   (0)|
|   1 |  TABLE ACCESS BY INDEX ROWID| T        |    115 |    41   (0)|
|*  2 |   INDEX RANGE SCAN          | I_T_NAME |     46 |     1   (0)|
----------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("NAME"=:X)
Note
-----
   - dynamic sampling used for this statement (level=2)
   - Warning: basic plan statistics not available. These are only collected when:
       * hint 'gather_plan_statistics' is used for the statement or
       * parameter 'statistics_level' is set to 'ALL', at session or system level

21 rows selected.


--建立函数索引
SQL> create index if_t_name on t(substr(name,1,5));
Index created.

SQL> explain plan for select * from t where name=:x;
Explained.

SQL> select * from table(dbms_xplan.display(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS cost'));
PLAN_TABLE_OUTPUT
---------------------------
Plan hash value: 1766912905
----------------------------------------------------------------------
| Id  | Operation                   | Name     | E-Rows | Cost (%CPU)|
----------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |      1 |     2   (0)|
|   1 |  TABLE ACCESS BY INDEX ROWID| T        |      1 |     2   (0)|
|*  2 |   INDEX RANGE SCAN          | I_T_NAME |      1 |     1   (0)|
----------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("NAME"=:X)
       filter(SUBSTR("NAME",1,5)=SUBSTR(:X,1,5))
Note
-----
   - dynamic sampling used for this statement (level=2)
   - Warning: basic plan statistics not available. These are only collected when:
       * hint 'gather_plan_statistics' is used for the statement or
       * parameter 'statistics_level' is set to 'ALL', at session or system level
22 rows selected.

--如果按照执行计划的内容,实际上sql语句被转化为如下:
select * from t where name=:x and SUBSTR("NAME",1,5)=SUBSTR(:X,1,5);
--实际的执行计划好像没用filter(SUBSTR("NAME",1,5)=SUBSTR(:X,1,5)).explain 看带绑定的sql语句至少说明这样的计划是可行的.
--不使用环境变量看看.
SQL> select id,name from t where rownum=1;
        ID NAME
---------- ----------------
         1 23C1W6VWH59ORH12

SQL> select * from t where name='23C1W6VWH59ORH12';
        ID NAME             TEXT
---------- ---------------- ---------
         1 23C1W6VWH59ORH12        x

SQL> @dpc
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  cybmv2qjraur2, child number 0
-------------------------------------
select * from t where name='23C1W6VWH59ORH12'
Plan hash value: 1766912905
----------------------------------------------------------------------
| Id  | Operation                   | Name     | E-Rows | Cost (%CPU)|
----------------------------------------------------------------------
|   0 | SELECT STATEMENT            |          |        |     2 (100)|
|   1 |  TABLE ACCESS BY INDEX ROWID| T        |      3 |     2   (0)|
|*  2 |   INDEX RANGE SCAN          | I_T_NAME |      1 |     1   (0)|
----------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - access("NAME"='23C1W6VWH59ORH12')
Note
-----
   - dynamic sampling used for this statement (level=2)
   - Warning: basic plan statistics not available. These are only collected when:
       * hint 'gather_plan_statistics' is used for the statement or
       * parameter 'statistics_level' is set to 'ALL', at session or system level

26 rows selected.


3.分析表看看:
SQL> exec dbms_stats.gather_table_stats(user,'t');
PL/SQL procedure successfully completed.

SQL> explain plan for select * from t where name=:x;
Explained.

SQL> select * from table(dbms_xplan.display(NULL,NULL,'ALLSTATS LAST PEEKED_BINDS cost'));
PLAN_TABLE_OUTPUT
---------------------------
Plan hash value: 3367455390
-----------------------------------------------------------------------
| Id  | Operation                   | Name      | E-Rows | Cost (%CPU)|
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |      1 |     2   (0)|
|*  1 |  TABLE ACCESS BY INDEX ROWID| T         |      1 |     2   (0)|
|*  2 |   INDEX RANGE SCAN          | IF_T_NAME |      1 |     1   (0)|
-----------------------------------------------------------------------

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

   1 - filter("NAME"=:X)
   2 - access(SUBSTR("NAME",1,5)=SUBSTR(:X,1,5))

Note
-----
   - Warning: basic plan statistics not available. These are only collected when:
       * hint 'gather_plan_statistics' is used for the statement or
       * parameter 'statistics_level' is set to 'ALL', at session or system level

21 rows selected.

--这个对于我是颠覆性的,查询name=:x条件,而可以使用IF_T_NAME的索引.

4.使用提示看看:
SQL> variable x varchar2(30);
SQL> exec :x := '23C1W6VWH59ORH12';

PL/SQL procedure successfully completed.

SQL> select /*+ index (t if_t_name ) */ * from t where name=:x;
        ID NAME             TEXT
---------- ---------------- -------
         1 23C1W6VWH59ORH12       x  

SQL> @dpc
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  337p0r8k0cmkb, child number 0
-------------------------------------
select /*+ index (t if_t_name ) */ * from t where name=:x

Plan hash value: 3367455390

-----------------------------------------------------------------------
| Id  | Operation                   | Name      | E-Rows | Cost (%CPU)|
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |        |     2 (100)|
|*  1 |  TABLE ACCESS BY INDEX ROWID| T         |      1 |     2   (0)|
|*  2 |   INDEX RANGE SCAN          | IF_T_NAME |      1 |     1   (0)|
-----------------------------------------------------------------------
Peeked Binds (identified by position):
--------------------------------------
   1 - (VARCHAR2(30), CSID=852): '23C1W6VWH59ORH12'
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("NAME"=:X)
   2 - access("T"."SYS_NC00004$"=SUBSTR(:X,1,5))
Note
-----
   - Warning: basic plan statistics not available. These are only collected when:
       * hint 'gather_plan_statistics' is used for the statement or
       * parameter 'statistics_level' is set to 'ALL', at session or system level
31 rows selected.

--可以发现使用提示在查询name=:x 这样的条件时,可以IF_T_NAME这样的函数索引.

--删除索引i_t_name,再看看情况如何呢?

SQL> drop index i_t_name;

Index dropped.

SQL> select  * from t where name=:x;
        ID NAME             TEXT
---------- ---------------- ------------------------------------------------------
         1 23C1W6VWH59ORH12                                                       

SQL> @dpc
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID  69yvh72cyvwsx, child number 0
-------------------------------------
select  * from t where name=:x

Plan hash value: 3367455390

-----------------------------------------------------------------------
| Id  | Operation                   | Name      | E-Rows | Cost (%CPU)|
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT            |           |        |     2 (100)|
|*  1 |  TABLE ACCESS BY INDEX ROWID| T         |      1 |     2   (0)|
|*  2 |   INDEX RANGE SCAN          | IF_T_NAME |      1 |     1   (0)|
-----------------------------------------------------------------------
Peeked Binds (identified by position):
--------------------------------------
   1 - (VARCHAR2(30), CSID=852): '23C1W6VWH59ORH12'
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - filter("NAME"=:X)
   2 - access("T"."SYS_NC00004$"=SUBSTR(:X,1,5))
Note
-----
   - Warning: basic plan statistics not available. These are only collected when:
       * hint 'gather_plan_statistics' is used for the statement or
       * parameter 'statistics_level' is set to 'ALL', at session or system level
31 rows selected.

--这样可以得出结论,查询name=:x这样的条件,可以通过建立substr(name,1,5)这样的函数索引来访问.
--这样可以想象一种情况,比如name的长度很长,如果前面的几个字符选择性很好,可以通过使用substr函数建立这样的索引来访问表.好处就是这样索引可以建立的很小.