Oracle之函数索引
在Oracle中,有一类特殊的索引,称为函数索引(Function-Based Indexes,FBI),它基于对表中列进行计算后的结果创建索引。函数索引在不修改应用程序的逻辑基础上提高了查询性能。如果没有函数索引,那么任何在列上执行了函数的查询都不能使用这个列的索引。当在查询中包含该函数时,数据库才会使用该函数索引。函数索引可以是一个B-Tree索引或位图索引。
用于生成索引的函数可以是算术表达式,也可以是一个包含SQL函数、用户定义PL/SQL函数、包函数,或C调用的表达式。当数据库处理INSERT和UPDATE语句时,它仍然必须计算函数才能完成对语句的处理。
对于函数索引的索引列的函数查询可以通过视图DBA_IND_EXPRESSIONS来实现,通过如下的SQL语句可以查询所有的函数索引:
SELECT * FROM DBA_INDEXES D WHERE D.INDEX_TYPE LIKE 'FUNCTION-BASED%';
函数索引必须遵守下面的规则:
① 必须使用基于成本的优化器,而且创建后必须对索引进行分析。
② 如果被函数索引所引用的用户自定义PL/SQL函数失效了或该函数索引的属主没有了在函数索引里面使用的函数的执行权限,那么对这张表上的执行的所有的操作(例如SELECT查询、DML等)也将失败(会报错:ORA-06575: Package or function F_R1_LHR is in an invalid state或ORA-00904: : invalid identifier)。这时,可以重新修改自定义函数并在编译无报错通过后,该表上所有的DML和查询操作将恢复正常。
③ 创建函数索引的函数必须是确定性的。即,对于指定的输入,总是会返回确定的结果。
④ 在创建索引的函数里面不能使用SUM、COUNT等聚合函数。
⑤ 不能在LOB类型的列、NESTED TABLE列上创建函数索引。
⑥ 不能使用SYSDATE、USER等非确定性函数。
⑦ 对于任何用户自定义函数必须显式的声明DETERMINISTIC关键字,否则会报错:“ora-30553: the function is not deterministic”。
需要注意的是,使用函数索引有几个先决条件:
(1)必须拥有CREATE INDEX和QUERY REWRITE(本模式下)或CREATE ANY INDEX和GLOBAL QUERY REWRITE(其它模式下)权限。其赋权语句分别为“GRANT QUERY REWRITE TO LHR;”和“GRANT GLOBAL QUERY REWRITE TO LHR;”。
(2)必须使用基于成本的优化器,基于规则的优化器将被忽略。
(3)参数QUERY_REWRITE_INTEGRITY和QUERY_REWRITE_ENABLED可以保持默认值。
QUERY_REWRITE_INTEGRITY = ENFORCED
QUERY_REWRITE_ENABLED = TRUE(从Oracle 10g开始默认为TRUE)
这里举一个基于函数的索引的例子。
首先为函数索引的建立及数据做准备:
SYS@lhrdb> CREATE TABLE TESTFINDEX_LHR(ID NUMBER,SCHR VARCHAR2(10));
Table created.
SYS@lhrdb> CREATE INDEX IND_FUN ON TESTFINDEX_LHR(UPPER(SCHR));
Index created.
SYS@lhrdb> INSERT INTO TESTFINDEX_LHR VALUES(1,'a');
1 row created.
SYS@lhrdb> COMMIT;
Commit complete.
因为强制使用基于规则的优化器,所以,不会使用函数索引:
SYS@lhrdb> SELECT /*+ RULE*/ * FROM TESTFINDEX_LHR WHERE UPPER(SCHR)='A';
ID SCHR
---------- ----------
1 a
Execution Plan
----------------------------------------------------------
Plan hash value: 940247041
--------------------------------------------
| Id | Operation | Name |
--------------------------------------------
| 0 | SELECT STATEMENT | |
|* 1 | TABLE ACCESS FULL| TESTFINDEX_LHR |
--------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(UPPER("SCHR")='A')
Note
-----
- rule based optimizer used (consider using cbo)
这里优化器选择了全表扫描,若在不使用基于规则的优化器的情况下,则该查询会选择函数索引IND_FUN:
SYS@lhrdb> SELECT * FROM TESTFINDEX_LHR WHERE UPPER(SCHR)='A';
ID SCHR
---------- ----------
1 a
Execution Plan
----------------------------------------------------------
Plan hash value: 967513602
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | 1 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TESTFINDEX_LHR | 1 | 27 | 1 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IND_FUN | 1 | | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(UPPER("SCHR")='A')
Note
-----
- dynamic sampling used for this statement (level=2)
SYS@lhrdb> SELECT D.TABLE_NAME,D.COLUMN_EXPRESSION FROM DBA_IND_EXPRESSIONS D WHERE D.INDEX_NAME='IND_FUN';
TABLE_NAME COLUMN_EXPRESSION
------------------------------ ----------------------
TESTFINDEX_LHR UPPER("SCHR")
可见,例子中使用了IND_FUN函数索引,且函数可以通过视图DBA_IND_EXPRESSIONS来查询。
我们进行数据库检索优化的方法,通常是对特定条件列加索引,减少由于全表扫描带来的逻辑物理IO消耗。索引的种类很多,我们经常使用的B*树索引,由于结构简单、适应性强,可以应对大多数数据访问优化需求。除B*树索引外,其他一些索引类型,也在一些场合中扮演着独特的地位。本篇来介绍其中的函数索引。
1、从B*树索引的失效谈起
和通常一样,我们准备实验环境。
SQL> select * from v$version where rownum<2;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production
SQL> create table t as select * from dba_objects;
Table created
//构建两个索引用作实验对象
SQL> create index idx_t_owner on t(owner);
Index created
SQL> create index idx_t_ddlt on t(last_ddl_time);
Index created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
环境中,我们在数据表T上建立了一般意义的索引。当我们进行检索的时候,CBO会适时选择合适的索引执行计划。
SQL> explain plan for select * from t where owner='SCOTT';
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1516787156
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2419 | 229K| 71 (0)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 2419 | 229K| 71 (0)|
|* 2 | INDEX RANGE SCAN | IDX_T_OWNER | 2419 | | 6 (0)|
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("OWNER"='SCOTT')
14 rows selected
但是,很多时候,我们可能会遇到在where条件里对索引列进行函数处理的情况。比如,选择owner列取值第二个字母是“c”的数据列,或者选取在特定天进行ddl操作的对象信息。这样的情况下,直接的想法就是在where条件列中加入列函数处理,但是这样做,会带来B*树索引的失效问题。
SQL> explain plan for select * from t where substr(owner,2,1)='C';
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1601196873
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 726 | 70422 | 283 (1)| 00:00:04 |
|* 1 | TABLE ACCESS FULL| T | 726 | 70422 | 283 (1)| 00:00:04 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(SUBSTR("OWNER",2,1)='C')
13 rows selected
在对条件列owner进行substr操作之后,生成的执行计划就不会带有索引路径,出现全表扫描。如果列上的B*树普通索引就是为该查询对应的用例服务的,那么这个索引的存在就失去了意义。
那么,这种时候应该如何处理呢?答案是:在SQL语句本身不存在重构优化的空间时(此种情况通常出现在系统的运维阶段),可以考虑使用函数索引来解决问题。
2、函数索引
函数索引与通常B*树索引的结构,存在很大相似性。区别就在于形成树结构的叶子节点上,保存的不是索引列的取值,而是经过特定的函数处理过的索引列值。
这样的结构,进行搜索的时候,就可以直接使用到函数索引的叶子节点,获取到对应的rowid集合。要求是出现于构建函数索引完全相同的函数条件。
首先,我们来构建函数索引。
SQL> create index idx_t_ownerf on t(substr(owner,2,1));
Index created
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
PL/SQL procedure successfully completed
构建函数索引的语法和一般索引的语法没有过多的区别,最大的差异就是在声明索引列的位置上,写清楚应用的函数语句。此时,数据字典视图系列中,已经反映出函数索引的不同。
SQL> select index_type from dba_indexes where index_name='IDX_T_OWNERF';
INDEX_TYPE
---------------------------
FUNCTION-BASED NORMAL
此时,我们再进行查询,执行计划会发生变化。
SQL> explain plan for select * from t where substr(owner,2,1)='C';
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2485331276
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4839 | 467K| 135 (0)
| 1 | TABLE ACCESS BY INDEX ROWID| T | 4839 | 467K| 135 (0)
|* 2 | INDEX RANGE SCAN | IDX_T_OWNERF | 4839 | | 9 (0)
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(SUBSTR("OWNER",2,1)='C')
14 rows selected
加入函数索引之后,我们可以发现同样的SQL语句,执行计划发生变化。函数索引开始起效。
那么,函数索引的本质是什么呢?我们检查数据字典视图,就可以发现函数索引的本质。
SQL> col table_name for a20;
SQL> col table_owner for a20;
SQL> col column_name for a30;
SQL> select table_owner, table_name, column_name from dba_ind_columns where index_name='IDX_T_OWNERF';
TABLE_OWNER TABLE_NAME COLUMN_NAME
-------------------- -------------------- ------------------------------
SYS T SYS_NC00016$
SQL> select column_expression from dba_ind_expressions where index_name = 'IDX_T_OWNERF';
COLUMN_EXPRESSION
-------------------------------------
SUBSTR("OWNER",2,1)
SQL> select column_name,data_type,data_default from dba_tab_cols where wner='SYS' and table_name='T' and column_name='SYS_NC00016$';
COLUMN_NAME DATA_TYPE DATA_DEFAULT
-------------------- -------
SYS_NC00016$ VARCHAR2 SUBSTR("OWNER",2,1)
检查了三个视图的情况,我们可以清楚的看出Oracle函数索引的本质。Oracle建立函数索引之后,就会先建立出一个不可见的内部列(SYS_NC00016$)。之后,对这个列建立普通的B*树索引。为了保证该列在不受影响的情况下进行数据生成,使用默认值技术,在数据插入或者变化的时候,进行同步。
3、函数索引使用
函数索引是一种很特殊的索引类型,可以应对开发阶段出现的对数据列加函数处理SQL优化。但是,笔者以为,函数索引的使用还是应当注意一些细节的,在大部分场合下,函数索引可以作为一种应急或者是不得为之的策略。
首先,函数索引的综合消耗要大于普通的B*树索引。相对于传统索引,函数索引要保证创造的函数列数据一致性和多次进行函数计算。这样的消耗要远大于普通B*树索引;
其次,函数索引的适应范围较小。函数索引其效果的最大要素就是函数的使用和定义是100%相同。如第二部分的例子中,取字符串的第二位字串。如果有一个变更的需求,要求取第三位,这样原来的那个函数索引就不能发挥效应了。而相对来说,普通的B*树索引参与各种SQL的能力要很多。应该说,函数索引的针对性很强,如果这个需求不属于关键需求,这样性价比略差。
最后,函数索引通常是一种事后补救措施。笔者认为,一个良好设计的应用,一个划分合理的数据库逻辑结构,应该是可以避免函数操作数据列的SQL大量出现的。只有在系统上线之后,开发团队开发的问题暴露出来,但是也没有精力进行修改时,运维人员才开始使用函数索引,保证系统功能能够实现。
对开发人员和开发DBA而言,函数索引通常是不得已为之的方案,要保证在SQL和数据表结构权衡无效的情况下,再考虑使用函数索引。
首先,考虑SQL结构的优化。这个方法可以消灭掉很多看似不得不使用函数索引的场合。如字符串类型比较、日期匹配等等,都可以通过代码检查和SQL改写来避免进入函数索引的状况。下面一个例子:
//获取前一天进行ddl操作的对象列表
SQL> select count(*) from t where trunc(last_ddl_time)=trunc(sysdate)-1;
COUNT(*)
----------
9
日期型操作最大的问题就是时分秒结构的处理。Date类型本身是带有时分秒信息的,而进行查询的时候,常常是使用特定的年月日。这样,就会带来一些检索条件的问题。很多开发人员,就是直接使用trunc函数,将数据列上的时分秒信息进行裁剪。这样的确简单,而且满足需求。但是也留下了列索引失效的隐患。
正确的解决方式,应该将SQL进行改写,变等于条件为范围条件。如下:
SQL> explain plan for select count(*) from t where last_ddl_time between to_date('2011-5-20 00:00:00','yyyy-mm-dd hh24:mi:ss')
2 and to_date('2011-5-20 23:59:59','yyyy-mm-dd hh24:mi:ss');
Explained
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3824876144
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 8 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 8 | | |
|* 2 | INDEX RANGE SCAN| IDX_T_DDLT | 91 | 728 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("LAST_DDL_TIME">=TO_DATE(' 2011-05-20 00:00:00',
'syyyy-mm-dd hh24:mi:ss') AND "LAST_DDL_TIME"<=TO_DATE(' 2011-05-2
23:59:59', 'syyyy-mm-dd hh24:mi:ss'))
16 rows selected
经过改写,没有使用函数索引,原有的B*树索引起效。很多时候,经过SQL的重新思考,是可以避免函数索引使用场合出现的。特别是在项目的开发阶段,这个尤为重要。
其次,就是对设计表的改进。我们常说一范式:列不可分。如果出现很多的对数据列的函数处理,我们就需要重新审视我们的设计表方案。是不是存在设计不合理、没有考虑到实际业务技术需求的方面。当SQL没有优化空间时,设计表的重构,冗余字段的加入可能是比较好的思路方法。
4、结论
本篇从一般的函数索引,谈到了SQL的改写和设计表优化。核心要义就是一点,慎用函数索引。而且,在绝大多数的情况下,我们是不需要使用函数索引的。只要能够理智冷静的分析实际需求和SQL结构,通常都可以获取到一个折中的方案。
Oracle中利用函数索引处理数据倾斜案例
首先声明:本方法是受到dbsnake的指导,再次感谢指点。
通常来说,索引选取的数据列最好为分散度高、选择性好。从索引树结构的角度看,列值都是分布在叶节点位置。这样,通过树结构搜索得到的叶节点数量效率比较高。
实际中,我们常常遇到数据列值倾斜的情况。就是说,整个列数据取值有限。但是大部分数据值都集中在少数一两个取值里,其他取值比例极少。比如:一个数据列值有“N”、“B”、“M”、“P”、“Q”几个取值,其中55%数据行取值为“N”,40%数据行取值为“B”,剩下的取值分布在5%的数据行中。对于这种结构的数据列加索引,是存在一些问题的。
首先,默认数据库是会为所有的列值(非空)建立索引结构。也就意味着无论是高频度取值,还是低频度取值,都会在索引结构的叶节点上出现。当然,这样的大部分叶节点都是这些重复值。
其次,在CBO(基于成本优化器)的作用下,对高频度取值的搜索一般都不会选择索引作为搜索路径,因为进行全表扫描可能效率更高。我们为数据列建立了索引,但高频词的查询永远不会走到索引路径。
最后,建立的索引空间和时间消耗比较大。建立的索引涵盖所有取值,对海量数据表而言,占有的空间势必较大。同时,在进行小频度数据查询的时候,虽然会去走索引路径,但是引起的逻辑物理读也是有一些损耗。
引入一个解决方法,思路:既然高频度值在查询的时候不会走到索引路径,可以考虑将其剔出构建索引的过程,只为那些低频度数据值建立索引结构。这样,建立的索引树结构相对较小,而且索引查询的效率也能提升。
具体的方法是使用decode函数。decode(a,b,c,d,e…f)含义:如果a=b,则返回c,等于d,返回e,最后没有匹配的情况下,返回f。针对上面的例子,可以使用decode(列名,‘N’, null, ‘B’, null, 列名),含义是,如果该列取值为N或者B,直接设置为null,否则才返回列值。并且以此建立函数索引。
这样做借助了Oracle两个功能:1、对null值不生成索引;2、函数索引;
下面的实验证明了该方法:
1、 构建数据环境
//数据准备
SQL> create table t as select * from dba_objects where 1=0;
Table created
//构造大数据环境,使用脚本
declare
i number;
begin
for i in 1..40 loop
insert /*+ append */ into t
select * from dba_objects;
commit;
end loop;
end;
/
SQL> select count(*) from t;
COUNT(*)
----------
4759209
Executed in 15.522 seconds
整理后的数据环境如下:
//投入实验的数据状态
SQL> select secondary, count(*) from t group by secondary;
SECONDARY COUNT(*)
--------- ----------
W 273
Q 9
D 273
T 421230
J 1866592
E 99
S 2470733
7 rows selected
Executed in 18.002 seconds
可以看到,近五百万数据两种,绝大部分数据集中到了S、T、J上,其他数据取值频数较小。数据倾斜趋势明显。
2、 建索引
分别对secondary列建立常规、函数索引。
SQL>create index IND_SEC_NORMAL on t(secondary);
Index created
SQL> create index ind_t_fun on t(decode (secondary, 'S', null, 'J', null, 'T', null, secondary ));
Index created
Executed in 28.049 seconds
索引ind_t_fun将S、T、J值转化为null,剔出了建立索引的过程。从索引段信息看,两个索引所占的空间差异比较大,也证明了这点。
SQL> select * from dba_segments where segment_name='IND_SEC_NORMAL';
OWNER SEGMENT_NAME SEGMENT_TYPE BYTES BLOCKS EXTENTS
------- ------------- ------------------ ---------- ---------- ----------
SYS IND_T_FUN INDEX 75497472 9216 80
Executed in 0.733 seconds
SQL> select * from dba_segments where segment_name=upper('ind_t_fun');
OWNER SEGMENT_NAME SEGMENT_TYPE BYTES BLOCKS EXTENTS
------ ------------- -------------- ---------- ---------- ----------
SYS IND_T_FUN INDEX 65536 8 1
Executed in 0.156 seconds
注:本结果经过额外处理,用于方便显示;
可以看出,同样是对一个数据列加索引。普通索引类型Ind_sec_normal占据80个区,9216个数据块,空间约占75.5M。而函数索引ind_t_fun的空间只用了初始分配的1个区,8个数据块,空间约占65K。由此,空间优势立现!
收集统计数据,由于是实验性质,而且数据量大,采用高采样率收集统计信息。
SQL> exec dbms_stats.gather_table_stats(user, 'T', cascade => true, estimate_percent => 100,method_opt => 'for all indexed columns');
PL/SQL procedure successfully completed
Executed in 60.403 seconds
3、 检索效率分析
针对数据量273的W取值进行分析。
直接索引搜索:
SQL> select * from t where secondary='W';
已选择273行。
已用时间: 00: 00: 00.37
执行计划
----------------------------------------------------------
Plan hash value: 1573525374
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 273 | 25935 | 11 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 273 | 25935 | 11 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IND_SEC_NORMAL | 273 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("SECONDARY"='W')
统计信息
----------------------------------------------------------
775 recursive calls
0 db block gets
272 consistent gets
21 physical reads
0 redo size
28339 bytes sent via SQL*Net to client
583 bytes received via SQL*Net from client
20 SQL*Net roundtrips to/from client
16 sorts (memory)
0 sorts (disk)
273 rows processed
发现采用W作为搜索值时,是进行了索引搜索。下面是用函数索引搜索进行对比。
SQL> select * from t where decode(secondary,'S',null,'J',null,'T',null,secondary)='W';
已选择273行。
已用时间: 00: 00: 00.04
执行计划
----------------------------------------------------------
Plan hash value: 3192598969
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 273 | 25935 | 116 (0)| 00:00:02 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 273 | 25935 | 116 (0)| 00:00:02 |
|* 2 | INDEX RANGE SCAN | IND_T_FUN | 273 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access(DECODE("SECONDARY",'S',NULL,'J',NULL,'T',NULL,"SECONDARY")='W')
统计信息
----------------------------------------------------------
45 recursive calls
0 db block gets
140 consistent gets
0 physical reads
0 redo size
13225 bytes sent via SQL*Net to client
583 bytes received via SQL*Net from client
20 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
273 rows processed
对比后,我们可以发现,使用函数索引的方法,在执行时间、物理逻辑读、CPU使用上有一定差异。
|
普通索引 |
函数索引 |
执行时间 |
00: 00: 00.37 |
00: 00: 00.04 |
CPU使用 |
11 |
116 |
consistent gets |
272 |
140 |
physical reads |
21 |
0 |
结论:使用函数索引处理偏值方法,在一定长度上优化查询效率和索引结构。上表的数据表明,会使逻辑物理读的消耗很大程度的减少(索引结构简化),同时连带影响执行时间的缩小。因为使用函数要进行计算,CPU使用率相对较高,在可以接受的范围内。
但是,这种方法是存在一些限制的,应用前一定要仔细规划。
首先,数据表数据要保证较大。因为毕竟函数索引的建立和搜索较普通索引消耗大,如果数据表小,带来的优化程度不能弥补消耗的成本,结果可能得不偿失。笔者进行的一系列实验中,也发现在数据量中等偏小时,这种性能优势不能凸显。
其次,列值倾斜趋势明显。通过开篇的讨论我们不难发现,列值倾斜的程度越高,使用函数索引剔出的数据量也就越大,生成的索引树结构也就越小越优化。这一点是本方法的核心!
最后,使用函数索引搜索时,搜索的取值频数越高,优化效果越好。在本例中,取值W的列有273行,可以看出明显的性能优化。当我们选择值有9条数据的Q值时,这种优化趋势可以看到,但是明显程度降低(实验结果略)。这里的原因可能是数据量小时,两种方法逻辑物理读的差异度缩小。
About Me
.............................................................................................................................................
● 本文作者:小麦苗,部分内容整理自网络,若有侵权请联系小麦苗删除
● 本文在itpub(http://blog.itpub.net/26736162/abstract/1/)、博客园(http://www.cnblogs.com/lhrbest)和个人微信公众号(xiaomaimiaolhr)上有同步更新
● 本文itpub地址:http://blog.itpub.net/26736162/abstract/1/
● 本文博客园地址:http://www.cnblogs.com/lhrbest
● 本文pdf版、个人简介及小麦苗云盘地址:http://blog.itpub.net/26736162/viewspace-1624453/
● 数据库笔试面试题库及解答:http://blog.itpub.net/26736162/viewspace-2134706/
● DBA宝典今日头条号地址:http://www.toutiao.com/c/user/6401772890/#mid=1564638659405826
.............................................................................................................................................
● QQ群号:230161599(满)、618766405
● 微信群:可加我微信,我拉大家进群,非诚勿扰
● 联系我请加QQ好友(646634621),注明添加缘由
● 于 2017-11-01 09:00 ~ 2017-11-30 22:00 在魔都完成
● 文章内容来源于小麦苗的学习笔记,部分整理自网络,若有侵权或不当之处还请谅解
● 版权所有,欢迎分享本文,转载请保留出处
.............................................................................................................................................
● 小麦苗的微店:https://weidian.com/s/793741433?wfr=c&ifr=shopdetail
● 小麦苗出版的数据库类丛书:http://blog.itpub.net/26736162/viewspace-2142121/
.............................................................................................................................................
使用微信客户端扫描下面的二维码来关注小麦苗的微信公众号(xiaomaimiaolhr)及QQ群(DBA宝典),学习最实用的数据库技术。
小麦苗的微信公众号 小麦苗的DBA宝典QQ群2 《DBA笔试面宝典》读者群 小麦苗的微店
.............................................................................................................................................