Oracle优化器:星型转换
SQL> select * from v$version;BANNER ---------------------------------------------------------------------- Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - Production PL/SQL Release 11.2.0.1.0 - Production CORE 11.2.0.1.0 Production TNS for 32-bit Windows: Version 11.2.0.1.0 - Production NLSRTL Version 11.2.0.1.0 - ProductionSQL> select * from global_name;GLOBAL_NAME ----------------------------------- www.oracledatabase12g.comSQL> conn maclean/maclean Connected.SQL> select table_name,comments2 from dba_tab_comments3 where owner = 'SH'4 and table_name in ('SALES', 'CUSTOMERS', 'CHANNELS', 'TIMES');TABLE_NAME COMMENTS ------------------------------ -------------------------------------------------------------------------------- CHANNELS small dimension table CUSTOMERS dimension table SALES facts table, without a primary key; all rows are uniquely identified by the comb TIMES 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 a35 SQL> col description for a45 SQL> col value for a8 SQL> 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_amountFROM sh.sales s, sh.times t, sh.customers c, sh.channels chWHERE s.time_id = t.time_idAND s.cust_id = c.cust_idAND s.channel_id = ch.channel_idAND 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 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_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_amountFROM sh.sales s, sh.times t, sh.customers cWHERE s.time_id = t.time_idAND s.cust_id = c.cust_idAND c.cust_state_province = 'FL'AND t.calendar_quarter_desc IN ('2000-01', '2000-02', '1999-12')AND s.time_id IN(SELECT time_idFROM sh.timesWHERE 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_idFROM sh.channelsWHERE 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_ID FPD: 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条记录即可,从而避免了低效的全表扫描。
省略重复连接
因为子查询及位图树只是通过维度表上的过滤条件为事实表过滤掉大量的数据,所以从事实表上获取的相关数据仍可能需要重复一次和维度表的连接。省略重复连接的前提是维度表上所有的谓词都是半连接谓词子查询的一部分,And 由子查询检索到的列均唯一(unique) And 维度表的列不被select或group by涉及。在上例中无需对CHANNELS表再次连接的理由是没有select(或group by)CHANNEL表上的列,且channel_id列是唯一的。临时表转换
若在已知星型转换中重复连接维度表无法被省略的话,Oracle可以将对维度表的子查询结果集存储到内存中的全局临时表(global temporary table)上以避免重复扫描维度表。此外,因为将子查询的结果集物化了,故而若使用并行查询则每个并行子进程(slave)可以直接从物化结果集的临时表中获得数据,而不需要反复执行子查询。 试看以下示例,了解Oracle是如何利用物化临时表避免反复连接的: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" T1
ST: 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" T1
ST: 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表。因为我们在构建临时表时已经使用谓词条件(如上面的红字语句),故而我们无需对临时表再次过滤。如何启用星型查询
星型转换由初始化参数star_transformation_enabled控制,该参数可以有三种选项:- TRUE: Oracle优化器自动识别语句中的事实表和约束维度表并进行星型转换。这一切优化尝试都在CBO的藩篱内,优化器需要确定转换后的执行计划成本要低于不转换的执行计划;同时优化器还会尝试利用物化的临时表,如果那样真的好的话。
- False: 优化器不会考虑星型转换。
- TEMP_DISABLE:当一个维度表超过100个块时,"如果简单地设置star_transformation_enabled为TRUE来启用星型变换,那么会话会创建一个内存中的全局临时表(global temporary table)来保存已过滤的维度数据,这在过去会造成很多问题;"这里说的100个块其实是隐式参数_temp_tran_block_threshold(number of blocks for a dimension before we temp transform)的默认值,此外隐式参数_temp_tran_cache(determines if temp table is created with cache option,默认为TRUE)决定了这类临时表是否被缓存住;为了避免创建全局临时表可能带来的问题,就可以用到TEMP_DISABLE这个禁用临时表的选项,让优化器不再考虑使用物化的临时表。
总结
星型转换可以有效改善大的事实表与多个具有良好选择率的维度表间连接的查询。星型转换有效避免了全表扫描的性能窘境。它只fetch那些事实表上的"绝对"相关行。同时星型转换是基于CBO优化器的,Oracle能很好地认清使用该种转换是否有利。一旦维度表上的过滤无法有效减少需要从事实表上处理的数据集和时,那么可能全表扫描相对而言更为恰当。 以上我们力图通过一些简单的查询和执行计划来诠释星型转换的基本理念,但现实生产环境中实际的查询语句可能要复杂的多;举例而言如果查询涉及星型模型中的多个事实表的话,那么其复杂度就大幅提高了;如何正确构建事实表上的索引,收集相关列上的柱状图信息,在Oracle优化器无法正确判断的情况下循循善诱,都是大型数据仓库环境中DBA所面临的难题。
本文转自maclean_007 51CTO博客,原文链接:http://blog.51cto.com/maclean/1277672
相关文章:

php安装模式mod_php和Fastcgi的选择与对比
安装php又面临到了模式的选择,以前都是选择mod_php模式,因为这样安装比较方便哈,今天突然关心起FastCGI这种模式,败毒了一把,找到了一些关于mod_php和Fastcgi的选择与对比这方面的讨论,现在发出来留一个记号…

暴雨之后,评估与重建可以用这个数据集
作者:神经星星来源:HyperAI超神经场景描述:连续多日的暴雨给郑州和河南部分城市,带来了严重的洪涝灾害,牵动了全国人民的心。灾难面前,现阶段的科技和具体解决方案,也能发挥出关键作用。关键词&…

从jQuery的缓存到事件监听
很久以前,我还在cnblogs里面逛的时候就提出过一个问题(刚找了半天没找到)。不知道大家有没有发现,用jQuery选择器"选择"之后的DOM上会添加jQuery*********属性。 <DIV idd1 jQuery1294122065250"1">abc…

对一些品种数量校正
update sphwph set shl1-1 where spidSPH00002323 and piciJHAYMA00017433_5 and hwHWI00000022 update sphwph set shl-11 where spidSPH00002323 and piciJHAYMA00021159_2 and hwHWI00000022 3月7日调拨后少了398 update sphwph set shl0398 where piciJHAYMA00024241_1 an…

收藏喜+1!值得使用的100个Python小技巧
目前Python可以说是非常流行,在目前的编程语言中,Python的抽象程度是最高的,是最接近自然语言的,很容易上手。你可以用它来完成很多任务,比如数据科学、机器学习、Web开发、脚本编写、自动化等。下面,给大家…

一款基jquery超炫的动画导航菜单
今天给大家分享一款基jquery超炫的动画导航菜单。这款导航菜单,初始时页面中间一个按钮,单击按钮,菜单从左侧飞入页中。再次单击按钮,导航飞入左侧消息。动画效果很非常炫。一起看下效果图: 在线预览 源码下载 实现的…

如何用C#动态编译、执行代码
在开始之前,先熟悉几个类及部分属性、方法:CSharpCodeProvider、ICodeCompiler、CompilerParameters、CompilerResults、Assembly。 一、CSharpCodeProvider 提供对C#代码生成器和代码编译器的实例的访问。如果要动态生成VB代码,可…

【解决】MySql 5.6 运行崩溃错误
【解决】MySql 5.6 运行崩溃错误 最近弄了一台云主机,配置是20G磁盘空间,1G运行内存的Linux服务器。在上面安装了LAMP(RHEL7.2Apache2.4MySql5.6PHP5.6),然后搭建wordpress网站,可是网站运行没多久…

制作安全网站的checklist
原作者charlee、原始链接 http://tech.idv2.com/2008/04/19/secure-website-checklist/ fcicq最近在IPA上看到一篇安全相关的文章,它的最末尾有个checklist,于是催我把它翻译了。前几天比较忙,周末没什么事儿了,就翻译一下吧。 原…

百变冰冰!手把手教你实现CVPR2021最新妆容迁移算法
作者:小潘师兄来源:AI算法与图像处理简介在本文中,我们从不同的角度将妆容迁移问题分解为两步提取-分配过程。为此,我们提出了一种基于风格的可控GAN模型,该模型由三个部分组成,每个部分分别对应于目标风格…

Vlan 4096的限制原因
为什么80%的码农都做不了架构师?>>> VLAN配置的最大可能值为4094,它的由来如下所述: IEEE802.1q协议也就是“Virtual Bridged Local Area Networks”(虚拟桥接局域网,简称虚拟局域网)协议&#…

Hive 数据模型
Hive 数据模型 Hive 数据表有五种类型:内部表,外部表,分区表,桶表,视图表,默认以 tab 分隔 * MySQL (Oracle) 表默认以逗号分隔,因此,要想导入 MySQL(Oracle) 数据,需要设…

完整中英文世界国家级联下拉列表插件【前端版】
为什么80%的码农都做不了架构师?>>> 这个小东西是之前小项目上临时增加功能的产物,那时候在网上找了很久都没有能用的插件,要么是数据残缺少得可怜,还有就是实现手段非常低效不可维护那种,各种奇拔问题&am…

何时使用margin和padding?
margin和padding的意义相信大家都很清楚,可是在具体应用中,到底应该使用哪一个,就比较难于判断了。 这篇文章 说得挺清楚的,在这里翻译一下,供参考。 何时应当使用margin 需要在border外侧添加空白时。 空白处不需要…

10年IT老兵给新人程序员的几点建议
【CSDN 编者按】对于很多计算机专业的同学而言,“进大厂”已经成为毕业后职业道路的首选。但是面试官最喜欢什么样的应届生你知道吗?在校期间应该为找工作做哪些准备?除了技术好,在职场中还有哪些必备软实力?今天&…

asp.net文件上传下载的简单实现
使用FileUpload上传: protected void btnUpload_Click(object sender, EventArgs e) { if (FileUpload1.HasFile) { /*通过文件扩展名判断文件类型*/ string fileExt System.IO.Path.Ge…

JAVA数组的定义及用法
数组是有序数据的集合,数组中的每一个元素具有同样的数组名和下标来唯一地确定数组中的元素。 1. 一维数组 1.1 一维数组的定义 type arrayName[]; type[] arrayName; 当中类型(type)能够为Java中随意的数据类型,包含简单类型组合类型,数组名…

英特尔公布新技术路线图,将为 AWS、高通代工芯片
编译|刘春霖出品|AI科技大本营(ID:rgznai100)图源|IC photo今天英特尔宣布其旗下的工厂将开始制造高通芯片,并公布了公司有史以来最详细的制程工艺和封装技术路线图,希望在 2025 年前赶上台积电、三星电子。除了公布其近十多年来首个全新晶体管架构 Ribb…

epoll相关资料整理
http://www.cppblog.com/converse/archive/2008/10/13/63928.htmlepoll相关资料整理 学习epoll有一段时间了,最近终于有一个服务器采用了epoll模型,从中积累了一些epoll的资料.个人感觉目前可以找到的epoll相关的资料太少了,因为epoll仅被linux 2.6以上版本内核所支持,它的应用…

18.绝对路径和相对路径
什么是绝对路径(Absolute Pathname)? 1.绝对路径必定由“/”开头 2.绝对路径是为档案/文件的所在位置做指向 3.在任何时候,都可以用绝对路径来找到我们想要的文件 PS:绝对路径只对当前所在目录有效。 什么是相对路径&a…

IE的box模型显示bug
原作者charlee、原始链接http://tech.idv2.com/2007/01/02/ie-box-model-bug/以及本声明。 box模型即由<div>等块元素的 margin、padding、border、width、height 等属性构成的显示模型,它是CSS布局的基础。通过设置<div>的各种属性,可以得到…

AI 能匹敌程序员了吗?OpenAI 新研究展示 NLP 大模型的局限性
作者:Ben Dickson来源:数据实战派Codex在一篇新论文中,OpenAI 的研究人员展示了 Codex 的详细信息,它是一种生成软件源代码的深度学习模型。Codex 可以为 OpenAI 和 GitHub 联合开发的 “AI 配对程序员” 工具 Copilot 提供支持。…

MSLicensing中断远程桌面连接
---------------------------中断远程桌面连接---------------------------客户端无法建立跟远程计算机的连接。导致这个错误的可能的原因是:1) 远程计算机上的远程连接可能没有启用。2) 已超出远程计算机上的连接最大数。3) 建立连接时出现了一个网络错误。------------------…

如何恢复,迁移,添加, 删除 Voting Disks
如何恢复,迁移,添加, 删除 Voting Disks 恢复流程 在11gR2 之前,我们可以直接直接使用dd命令对voting disk进行备份。 DD示例 备份votedisk盘: [rootraw1 bin]# dd if/dev/raw/raw2 of/home/oracle/voting_disk.bak 恢复votedisk盘…

跨站脚本攻击(XSS)FAQ
原作者charlee、原始链接http://tech.idv2.com/2006/08/30/xss-faq/以及本声明。 该文章简单地介绍了XSS的基础知识及其危害和预防方法。Web开发人员的必读。译自 http://www.cgisecurity.com/articles/xss-faq.shtml。 简介 现在的网站包含大量的动态内容以提高用户体验&…

linux中的for命令
bash shell提供了for命令,用于创建通过一系列值重复的循环。每次重复使用系列中的一个值执行一个定义的命令集。for命令基本格式为:for var in listdo commandsdone1.读取列表中的值 #!/bin/bash #basic for command for test in a b c d e f doecho The…

终于有人把计算机视觉讲明白了 。。。
机器学习是目前比较热门的技术,包含深度学习、强化学习、对抗学习、对偶学习、迁移学习、分布式学习、以及元学习等内容。得益于大数据、大模型、大计算的发展,深度学习在计算机视觉、语音处理、自然语言方面相继取得了突破,达到甚至超过了人…

mysql启动与关闭(手动与自动)
手动管理mysql的启动与关闭 [rootmysql ~]# service mysql start --手动启动mysql Starting MySQL. SUCCESS! [rootmysql ~]# service mysql stop --手动关闭mysql Shutting down MySQL.. SUCCESS! [rootmysql ~]# mysqld --verbose --help --查看MySQL的默认参数的具体值 如果…

C#中抽象类和接口的区别
一、抽象类:抽象类是特殊的类,只是不能被实例化;除此以外,具有类的其他特性;重要的是抽象类可以包括抽象方法,这是普通类所不能的。抽象方法只能声明于抽象类中,且不包含任何实现,派…

echo向文件中写入
echo命令向一个文件写入内容的方法详解,感兴趣的朋友可以参考下。 覆盖型写法 (文件里原来的内容被覆盖)echo "aaa" > a.txtecho aaa > a.txt 添加型写法 (新内容添加在原来内容的后面)echo "aaa" >> a.txtecho aaa >…