本文共 23869 字,大约阅读时间需要 79 分钟。
SQL> select * from v$version;BANNER----------------------------------------------------------------------Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - ProductionPL/SQL Release 11.2.0.1.0 - ProductionCORE 11.2.0.1.0 ProductionTNS for 32-bit Windows: Version 11.2.0.1.0 - ProductionNLSRTL Version 11.2.0.1.0 - ProductionSQL> select * from global_name;GLOBAL_NAME-----------------------------------www.oracledatabase12g.comSQL> conn maclean/macleanConnected.SQL> select table_name,comments 2 from dba_tab_comments 3 where owner = 'SH' 4 and table_name in ('SALES', 'CUSTOMERS', 'CHANNELS', 'TIMES');TABLE_NAME COMMENTS------------------------------ --------------------------------------------------------------------------------CHANNELS small dimension tableCUSTOMERS dimension tableSALES facts table, without a primary key; all rows are uniquely identified by the combTIMES Time dimension table to support multiple hierarchies and materialized views可以从以上各表的注释(comment)中看到,SALES表是SH模式下一个没有主键的事实表,而CHANNELS、CUSTOMERS、TIMES三个小表充当维度表的角色。我们试着构建以下星型查询语句,该查询用以检索出从1999年12月至2000年2月间Florida州所有城市直销形式的每月销售额。
SQL> col name for a35SQL> col description for a45SQL> col value for a8SQL> select name,value,description from v$system_parameter where name='star_transformation_enabled';NAME VALUE DESCRIPTION----------------------------------- -------- ---------------------------------------------star_transformation_enabled FALSE enable the use of star transformation/* 初始化参数star_transformation_enabled用以控制如何启用星型转换, 默认为FALSE,该参数可以动态修改*/SELECT c.cust_city, t.calendar_quarter_desc, SUM(s.amount_sold) sales_amount FROM sh.sales s, sh.times t, sh.customers c, sh.channels ch WHERE s.time_id = t.time_id AND s.cust_id = c.cust_id AND s.channel_id = ch.channel_id AND c.cust_state_province = 'FL' AND ch.channel_desc = 'Direct Sales' AND t.calendar_quarter_desc IN ('2000-01', '2000-02','1999-12') GROUP BY c.cust_city, t.calendar_quarter_desc;SQL> select * from table(dbms_xplan.display_cursor(format => 'IOSTATS'));PLAN_TABLE_OUTPUT---------------------------------------------------------------------------------------------------------------SQL_ID ddjm7k72b8p2a, child number 1-------------------------------------SELECT /*+ gather_plan_statistics */ c.cust_city,t.calendar_quarter_desc, SUM(s.amount_sold) sales_amount FROMsh.sales s, sh.times t, sh.customers c, sh.channels ch WHERE s.time_id= t.time_id AND s.cust_id = c.cust_id AND s.channel_id =ch.channel_id AND c.cust_state_province = 'FL' ANDch.channel_desc = 'Direct Sales' AND t.calendar_quarter_desc IN('2000-01', '2000-02','1999-12') GROUP BY c.cust_city,t.calendar_quarter_descPlan hash value: 382868716---------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads |---------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | 24 |00:00:00.62 | 1735 | 1726 || 1 | HASH GROUP BY | | 1 | 24 | 24 |00:00:00.62 | 1735 | 1726 ||* 2 | HASH JOIN | | 1 | 1580 | 6015 |00:00:00.42 | 1735 | 1726 ||* 3 | TABLE ACCESS FULL | CUSTOMERS | 1 | 2438 | 2438 |00:00:01.73 | 1459 | 1455 ||* 4 | HASH JOIN | | 1 | 4575 | 74631 |00:00:00.18 | 276 | 271 || 5 | PART JOIN FILTER CREATE | :BF0000 | 1 | 227 | 182 |00:00:00.04 | 59 | 60 || 6 | MERGE JOIN CARTESIAN | | 1 | 227 | 182 |00:00:00.04 | 59 | 60 ||* 7 | TABLE ACCESS FULL | CHANNELS | 1 | 1 | 1 |00:00:00.01 | 3 | 6 || 8 | BUFFER SORT | | 1 | 227 | 182 |00:00:00.02 | 56 | 54 ||* 9 | TABLE ACCESS FULL | TIMES | 1 | 227 | 182 |00:00:00.02 | 56 | 54 || 10 | PARTITION RANGE JOIN-FILTER| | 1 | 117K| 117K|00:00:00.09 | 217 | 211 || 11 | TABLE ACCESS FULL | SALES | 2 | 117K| 117K|00:00:00.07 | 217 | 211 |---------------------------------------------------------------------------------------------------------------可以看到在以上不使用星型转换的执行计划中对事实表SALES执行了全表扫描,这是我们不希望发生的。因为SALES表中每一行记录都对应于一笔销售记录,因此其可能包含数百万行记录。但实际上这其中仅有极小部分是我们在查询中指定的季度在弗罗里达州直销的纪录。若我们启用星型转换,执行计划是否有所改善?
SQL> alter session set star_transformation_enabled=temp_disable;Session altered.SQL> alter session set events '10053 trace name context forever,level 1';Session altered.在我们的理想当中星型变化会将原查询语句转换成如下形式:SELECT c.cust_city, t.calendar_quarter_desc, SUM(s.amount_sold) sales_amount FROM sh.sales s, sh.times t, sh.customers c WHERE s.time_id = t.time_id AND s.cust_id = c.cust_id AND c.cust_state_province = 'FL' AND t.calendar_quarter_desc IN ('2000-01', '2000-02', '1999-12') AND s.time_id IN (SELECT time_id FROM sh.times WHERE calendar_quarter_desc IN ('2000-01', '2000-02', '1999-12')) AND s.cust_id IN (SELECT cust_id FROM sh.customers WHERE cust_state_province = 'FL') AND s.channel_id IN (SELECT channel_id FROM sh.channels WHERE channel_desc = 'Direct Sales') GROUP BY c.cust_city, t.calendar_quarter_desc;/* 以添加AND..IN的形式明确了利用组合过滤谓词来减少需要处理的数据集 */通过10053优化trace我们可以了解Oracle优化器是如何真正产生这部分过度谓词的:FPD: Considering simple filter push in query block SEL$C3AF6D21 (#1)"S"."CHANNEL_ID"=ANY (SELECT /*+ SEMIJOIN_DRIVER */ "CH"."CHANNEL_ID" FROM "SH"."CHANNELS" "CH")AND "S"."CUST_ID"=ANY (SELECT /*+ SEMIJOIN_DRIVER */ "C"."CUST_ID" FROM "SH"."CUSTOMERS" "C") AND"S"."TIME_ID"=ANY (SELECT /*+ SEMIJOIN_DRIVER */ "T"."TIME_IDFPD: Considering simple filter push in query block SEL$ACF30367 (#4)"T"."CALENDAR_QUARTER_DESC"='2000-01' OR "T"."CALENDAR_QUARTER_DESC"='2000-02' OR "T"."CALENDAR_QUARTER_DESC"='1999-12'try to generate transitive predicate from check constraints for query block SEL$ACF30367 (#4)finally: "T"."CALENDAR_QUARTER_DESC"='2000-01' OR "T"."CALENDAR_QUARTER_DESC"='2000-02' OR "T"."CALENDAR_QUARTER_DESC"='1999-12'FPD: Considering simple filter push in query block SEL$F6045C7B (#3)"C"."CUST_STATE_PROVINCE"='FL'try to generate transitive predicate from check constraints for query block SEL$F6045C7B (#3)finally: "C"."CUST_STATE_PROVINCE"='FL'FPD: Considering simple filter push in query block SEL$6EE793B7 (#2)"CH"."CHANNEL_DESC"='Direct Sales'try to generate transitive predicate from check constraints for query block SEL$6EE793B7 (#2)finally: "CH"."CHANNEL_DESC"='Direct Sales'try to generate transitive predicate from check constraints for query block SEL$C3AF6D21 (#1)finally: "S"."CHANNEL_ID"=ANY (SELECT /*+ SEMIJOIN_DRIVER */ "CH"."CHANNEL_ID" FROM "SH"."CHANNELS" "CH")AND "S"."CUST_ID"=ANY (SELECT /*+ SEMIJOIN_DRIVER */ "C"."CUST_ID" FROM "SH"."CUSTOMERS" "C")AND "S"."TIME_ID"=ANY (SELECT /*+ SEMIJOIN_DRIVER */ "T"."TIME_IDFinal query after transformations:******* UNPARSED QUERY IS *******最终转换后的查询语句:SELECT "C"."CUST_CITY" "CUST_CITY", "T"."CALENDAR_QUARTER_DESC" "CALENDAR_QUARTER_DESC", SUM("S"."AMOUNT_SOLD") "SALES_AMOUNT" FROM "SH"."SALES" "S", "SH"."TIMES" "T", "SH"."CUSTOMERS" "C" WHERE "S"."CHANNEL_ID" = ANY (SELECT /*+ SEMIJOIN_DRIVER */ "CH"."CHANNEL_ID" "ITEM_1" FROM "SH"."CHANNELS" "CH" WHERE "CH"."CHANNEL_DESC" = 'Direct Sales') AND "S"."CUST_ID" = ANY (SELECT /*+ SEMIJOIN_DRIVER */ "C"."CUST_ID" "ITEM_1" FROM "SH"."CUSTOMERS" "C" WHERE "C"."CUST_STATE_PROVINCE" = 'FL') AND "S"."TIME_ID" = ANY (SELECT /*+ SEMIJOIN_DRIVER */ "T"."TIME_ID" "ITEM_1" FROM "SH"."TIMES" "T" WHERE "T"."CALENDAR_QUARTER_DESC" = '2000-01' OR "T"."CALENDAR_QUARTER_DESC" = '2000-02' OR "T"."CALENDAR_QUARTER_DESC" = '1999-12') AND "S"."TIME_ID" = "T"."TIME_ID" AND "S"."CUST_ID" = "C"."CUST_ID" AND "C"."CUST_STATE_PROVINCE" = 'FL' AND ("T"."CALENDAR_QUARTER_DESC" = '2000-01' OR "T"."CALENDAR_QUARTER_DESC" = '2000-02' OR "T"."CALENDAR_QUARTER_DESC" = '1999-12') GROUP BY "C"."CUST_CITY", "T"."CALENDAR_QUARTER_DESC"/* 要比我们想想的复杂一些,子查询将IN语句化解了, 并且AND...ANY的形式追加了过度谓词条件*/------------------------------------------------------------------+-----------------------------------+---------------+| Id | Operation | Name | Rows | Bytes | Cost | Time | Pstart| Pstop |------------------------------------------------------------------+-----------------------------------+---------------+| 0 | SELECT STATEMENT | | | | 1710 | | | || 1 | HASH GROUP BY | | 1254 | 77K | 1710 | 00:00:21 | | || 2 | HASH JOIN | | 1254 | 77K | 1283 | 00:00:16 | | || 3 | HASH JOIN | | 1254 | 45K | 877 | 00:00:11 | | || 4 | TABLE ACCESS FULL | TIMES | 227 | 3632 | 18 | 00:00:01 | | || 5 | PARTITION RANGE SUBQUERY | | 1254 | 26K | 858 | 00:00:11 | KEY(SUBQUERY)| KEY(SUBQUERY)|| 6 | TABLE ACCESS BY LOCAL INDEX ROWID | SALES | 1254 | 26K | 858 | 00:00:11 | KEY(SUBQUERY)| KEY(SUBQUERY)|| 7 | BITMAP CONVERSION TO ROWIDS | | | | | | | || 8 | BITMAP AND | | | | | | | || 9 | BITMAP MERGE | | | | | | | || 10 | BITMAP KEY ITERATION | | | | | | | || 11 | BUFFER SORT | | | | | | | || 12 | TABLE ACCESS FULL | CHANNELS | 1 | 13 | 3 | 00:00:01 | | || 13 | BITMAP INDEX RANGE SCAN | SALES_CHANNEL_BIX| | | | | KEY(SUBQUERY)| KEY(SUBQUERY)|| 14 | BITMAP MERGE | | | | | | | || 15 | BITMAP KEY ITERATION | | | | | | | || 16 | BUFFER SORT | | | | | | | || 17 | TABLE ACCESS FULL | TIMES | 227 | 3632 | 18 | 00:00:01 | | || 18 | BITMAP INDEX RANGE SCAN | SALES_TIME_BIX | | | | | KEY(SUBQUERY)| KEY(SUBQUERY)|| 19 | BITMAP MERGE | | | | | | | || 20 | BITMAP KEY ITERATION | | | | | | | || 21 | BUFFER SORT | | | | | | | || 22 | TABLE ACCESS FULL | CUSTOMERS | 2438 | 38K | 406 | 00:00:05 | | || 23 | BITMAP INDEX RANGE SCAN | SALES_CUST_BIX | | | | | KEY(SUBQUERY)| KEY(SUBQUERY)|| 24 | TABLE ACCESS FULL | CUSTOMERS | 2438 | 62K | 406 | 00:00:05 | | |------------------------------------------------------------------+-----------------------------------+---------------+Predicate Information:----------------------2 - access("S"."CUST_ID"="C"."CUST_ID")3 - access("S"."TIME_ID"="T"."TIME_ID")4 - filter(("T"."CALENDAR_QUARTER_DESC"='1999-12' OR "T"."CALENDAR_QUARTER_DESC"='2000-01' OR "T"."CALENDAR_QUARTER_DESC"='2000-02'))12 - filter("CH"."CHANNEL_DESC"='Direct Sales')13 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")17 - filter(("T"."CALENDAR_QUARTER_DESC"='1999-12' OR "T"."CALENDAR_QUARTER_DESC"='2000-01' OR "T"."CALENDAR_QUARTER_DESC"='2000-02'))18 - access("S"."TIME_ID"="T"."TIME_ID")22 - filter("C"."CUST_STATE_PROVINCE"='FL')23 - access("S"."CUST_ID"="C"."CUST_ID")24 - filter("C"."CUST_STATE_PROVINCE"='FL')从以上演示中可以看到,星型转换添加了必要的对应于维度表约束的子查询谓词。这些子查询谓词又被称为位图半连接谓词(bitmap semi-join predicates,见SEMIJOIN_DRIVER hint)。通过迭代来自于子查询的键值,再通过位图(bitmap)的AND、OR操作(这些位图可以源于位图索引bitmap index,但也可以取自普通的B*tree索引),我们可以做到仅仅访问事实表上的查询相关记录。理想状况下维度表上的过滤谓词可以帮我们过滤掉大量的数据,这样就可以使执行计划效率大大提升。当我们获取到事实表上的相关行后,这部分结果集可能仍需要同维度表使用原谓词重复连接(join back)。某些情况下,重复连接可以被省略,之后我们会提到。 如上演示中列出了星型转换后的查询语句的执行计划。这里可以看到Oracle是使用"TABLE ACCESS BY LOCAL INDEX ROWID"形式访问SALES事实表的,而非全表扫描。这里我们仅关心7-23行的执行计划,服务进程分别在(12,17,22)行从维度表中取得各维度表的相关键值(key value),同时对部分结果集执行了BUFFER SORT操作;在(13,18,23)行的'bitmap index range scan'操作中服务进程从事实表的三个对应于维度表外键的位图索引上(SALES_CHANNEL_BIX,SALES_TIME_BIX,SALES_CUST_BIX)获取了最原始的位图。位图上的每一个bit都对应于事实表上的一行记录。若从子查询中获取的键值(key values)与事实表上的值一致则bit置为1,否则为0。举例而言位图bitmap:[1][0][1][1][0][0][0]..[0](之后都为零)表示事实表上仅有第一、三、四行匹配于由子查询提供的键值。我们假设以上位图是由times表子查询提供的众多键值中的一个(如'2000-01')的对应于事实表的位图表达式。 接着在执行计划的(10,15,20)行上的'bitmap key iteration'操作会迭代每一个由子查询提供的键值并获取相应的位图。我们假设times表子查询提供的另外2个键值'2000-02'和'1999-12'分别对应的位图为[0][0][0][0][0][1]..[0]和[0][0][0][0][1][0]...[0]即每键值都只有一行符合。 毫无疑问ITERATION迭代操作会为我们生成众多位图,接下来需要对这些不同键值对应的位图进行位图合并操作(BITMAP MERGE,相当于对位图做OR操作),可以看到在上例执行计划中为(9,14,19)行;以我们假设的times表子查询位图合并而言,会生产一个简单的位图[1][0][1][1][1][1][0][0]..[0],这个位图对应事实表上的第一、三、四、五、六行,是对'2000-01','2000-02','1999-12'三个键值对应位图的合并。 在获得最后位图前我们还需要对来自于三个子查询的位图进一步处理,因为原始查询语句中各约束条件是AND与的形式,因此我们还要对这些已合并的位图执行AND与操作,如执行计划中的第八行"BITMAP AND",因为是AND与操作所以这步又会过滤掉大量记录。我们假设最终获得的位图是[1][0][1][0]...[0],即仅有第一、三行。 通过最终bitmap位图Oracle可以极高效地生成事实表的ROWID,此步骤表现为第七行的"BITMAP CONVERSION TO ROWIDS",我们使用这些ROWID来访问事实表取得少量的"绝对"相关记录。以我们的假设而言最终位图仅有2位为1,只需要用这2行的ROWID从事实表上直接fetch2条记录即可,从而避免了低效的全表扫描。
SQL> alter session set star_transformation_enabled=true;Session altered.SQL> alter session set events '10053 trace name context forever,level 1';Session altered.SELECT "T1"."C1" "CUST_CITY", "T"."CALENDAR_QUARTER_DESC" "CALENDAR_QUARTER_DESC", SUM("S"."AMOUNT_SOLD") "SALES_AMOUNT" FROM "SH"."SALES" "S", "SH"."TIMES" "T", "SYS"."SYS_TEMP_0FD9D660E_1DF5D6" "T1" WHERE "S"."CUST_ID" = ANY (SELECT /*+ SEMIJOIN_DRIVER CACHE_TEMP_TABLE ("T1") */ "T1"."C0" "C0" FROM "SYS"."SYS_TEMP_0FD9D660E_1DF5D6" "T1") AND "S"."CHANNEL_ID" = ANY (SELECT /*+ SEMIJOIN_DRIVER */ "CH"."CHANNEL_ID" "ITEM_1" FROM "SH"."CHANNELS" "CH" WHERE "CH"."CHANNEL_DESC" = 'Direct Sales') AND "S"."TIME_ID" = ANY (SELECT /*+ SEMIJOIN_DRIVER */ "T"."TIME_ID" "ITEM_1" FROM "SH"."TIMES" "T" WHERE "T"."CALENDAR_QUARTER_DESC" = '2000-01' OR "T"."CALENDAR_QUARTER_DESC" = '2000-02' OR "T"."CALENDAR_QUARTER_DESC" = '1999-12') AND "S"."TIME_ID" = "T"."TIME_ID" AND "S"."CUST_ID" = "T1"."C0" AND ("T"."CALENDAR_QUARTER_DESC" = '2000-01' OR "T"."CALENDAR_QUARTER_DESC" = '2000-02' OR "T"."CALENDAR_QUARTER_DESC" = '1999-12') GROUP BY "T1"."C1", "T"."CALENDAR_QUARTER_DESC"以上为启用临时表后的星型转换后的查询语句,相应的执行计划如下:---------------------------------------------------------------------------+-----------------------------------+---------------+| Id | Operation | Name | Rows | Bytes | Cost | Time | Pstart| Pstop |---------------------------------------------------------------------------+-----------------------------------+---------------+| 0 | SELECT STATEMENT | | | | 911 | | | || 1 | TEMP TABLE TRANSFORMATION | | | | | | | || 2 | LOAD AS SELECT | | | | | | | || 3 | TABLE ACCESS FULL | CUSTOMERS | 2438 | 62K | 406 | 00:00:05 | | || 4 | HASH GROUP BY | | 1254 | 64K | 506 | 00:00:07 | | || 5 | HASH JOIN | | 1254 | 64K | 479 | 00:00:06 | | || 6 | HASH JOIN | | 1254 | 45K | 475 | 00:00:06 | | || 7 | TABLE ACCESS FULL | TIMES | 227 | 3632 | 18 | 00:00:01 | | || 8 | PARTITION RANGE SUBQUERY | | 1254 | 26K | 456 | 00:00:06 | KEY(SUBQUERY)| KEY(SUBQUERY)|| 9 | TABLE ACCESS BY LOCAL INDEX ROWID | SALES | 1254 | 26K | 456 | 00:00:06 | KEY(SUBQUERY)| KEY(SUBQUERY)|| 10 | BITMAP CONVERSION TO ROWIDS | | | | | | | || 11 | BITMAP AND | | | | | | | || 12 | BITMAP MERGE | | | | | | | || 13 | BITMAP KEY ITERATION | | | | | | | || 14 | BUFFER SORT | | | | | | | || 15 | TABLE ACCESS FULL | CHANNELS | 1 | 13 | 3 | 00:00:01 | | || 16 | BITMAP INDEX RANGE SCAN | SALES_CHANNEL_BIX | | | | | KEY(SUBQUERY)| KEY(SUBQUERY)|| 17 | BITMAP MERGE | | | | | | | || 18 | BITMAP KEY ITERATION | | | | | | | || 19 | BUFFER SORT | | | | | | | || 20 | TABLE ACCESS FULL | TIMES | 227 | 3632 | 18 | 00:00:01 | | || 21 | BITMAP INDEX RANGE SCAN | SALES_TIME_BIX | | | | | KEY(SUBQUERY)| KEY(SUBQUERY)|| 22 | BITMAP MERGE | | | | | | | || 23 | BITMAP KEY ITERATION | | | | | | | || 24 | BUFFER SORT | | | | | | | || 25 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660E_1DF5D6| 2438 | 12K | 4 | 00:00:01 | | || 26 | BITMAP INDEX RANGE SCAN | SALES_CUST_BIX | | | | | KEY(SUBQUERY)| KEY(SUBQUERY)|| 27 | TABLE ACCESS FULL | SYS_TEMP_0FD9D660E_1DF5D6| 2438 | 36K | 4 | 00:00:01 | | |---------------------------------------------------------------------------+-----------------------------------+---------------+Predicate Information:----------------------3 - filter("C"."CUST_STATE_PROVINCE"='FL')5 - access("S"."CUST_ID"="C0")6 - access("S"."TIME_ID"="T"."TIME_ID")7 - filter(("T"."CALENDAR_QUARTER_DESC"='1999-12' OR "T"."CALENDAR_QUARTER_DESC"='2000-01' OR "T"."CALENDAR_QUARTER_DESC"='2000-02'))15 - filter("CH"."CHANNEL_DESC"='Direct Sales')16 - access("S"."CHANNEL_ID"="CH"."CHANNEL_ID")20 - filter(("T"."CALENDAR_QUARTER_DESC"='1999-12' OR "T"."CALENDAR_QUARTER_DESC"='2000-01' OR "T"."CALENDAR_QUARTER_DESC"='2000-02'))21 - access("S"."TIME_ID"="T"."TIME_ID")26 - access("S"."CUST_ID"="C0")从以上trace中可以看到系统命名的临时表SYS_TEMP_0FD9D660E_1DF5D6缓存CUSTOMERS表,之后原先CUSTOMERS表被SYS_TEMP_0FD9D660E_1DF5D6所取代,原CUSTOMERS表上的cust_id和cust_city列均被替换为别名为T1的临时表的C0和C1列。实际上该临时表也仅需要这2列即可满足计划的需求,所以该临时表以如下查询语句填充:
ST: Subquery text:******* UNPARSED QUERY IS *******SELECT "C"."CUST_ID" "ITEM_1","C"."CUST_CITY" "ITEM_2" FROM "SH"."CUSTOMERS" "C" WHERE "C"."CUST_STATE_PROVINCE"='FL'Copy query block qb# -1 () : SELECT /*+ CACHE_TEMP_TABLE(T1) */ "C0" FROM "SYS"."SYS_TEMP_0FD9D660E_1DF5D6" T1ST: Subquery (temp table) text:******* UNPARSED QUERY IS *******SELECT /*+ CACHE_TEMP_TABLE ("T1") */ "T1"."C0" "C0" FROM "SYS"."SYS_TEMP_0FD9D660E_1DF5D6" "T1"Copy query block qb# -1 () : SELECT /*+ CACHE_TEMP_TABLE(T1) */ "C0", "C1" FROM "SYS"."SYS_TEMP_0FD9D660E_1DF5D6" T1ST: Join back qbc text:******* UNPARSED QUERY IS *******SELECT /*+ CACHE_TEMP_TABLE ("T1") */ "T1"."C0" "C0","T1"."C1" "C1" FROM "SYS"."SYS_TEMP_0FD9D660E_1DF5D6" "T1"可以从以上执行计划中看到第一、二、三行的"TEMP TABLE TRANSFORMATION LOAD AS SELECT TABLE ACCESS FULL CUSTOMERS"看到Oracle是如何将子查询物化为临时表的。在第25行,Oracle直接以该临时表替代了子查询来构建我们所需要的位图。到第27行Oracle直接利用该临时表来重复连接,避免再次扫描customers表。因为我们在构建临时表时已经使用谓词条件(如上面的红字语句),故而我们无需对临时表再次过滤。
星型转换可以有效改善大的事实表与多个具有良好选择率的维度表间连接的查询。星型转换有效避免了全表扫描的性能窘境。它只fetch那些事实表上的"绝对"相关行。同时星型转换是基于CBO优化器的,Oracle能很好地认清使用该种转换是否有利。一旦维度表上的过滤无法有效减少需要从事实表上处理的数据集和时,那么可能全表扫描相对而言更为恰当。 以上我们力图通过一些简单的查询和执行计划来诠释星型转换的基本理念,但现实生产环境中实际的查询语句可能要复杂的多;举例而言如果查询涉及星型模型中的多个事实表的话,那么其复杂度就大幅提高了;如何正确构建事实表上的索引,收集相关列上的柱状图信息,在Oracle优化器无法正确判断的情况下循循善诱,都是大型数据仓库环境中DBA所面临的难题。
本文转自maclean_007 51CTO博客,原文链接:http://blog.51cto.com/maclean/1277672
转载地址:http://qbnpl.baihongyu.com/