Oracle SQL高级编程——子查询因子化全解析

来源:互联网 发布:sha1算法的c语言实现 编辑:程序博客网 时间:2024/05/16 05:35

参见《Oracle SQL高级编程》。

概述

子查询因子化就是ANSI中的公共表达式。
从11.2开始,子查询因子化开始支持递归。可以实现CONNECT BY的功能。

标准的子查询因子化的例子

这是一个非常复杂的查询,下面是不加因子化的版本。注意PIVOT的用法。

select * from (    select /*+ gather_plan_statistics */     product , channel , quarter , country , quantity_sold     from     (        select prod_name product , country_name country , channel_id channel ,            substr(calendar_quarter_desc , 6 , 2 ) quarter ,             sum(amount_sold) amount_sold , sum(quantity_sold) quantity_sold         from sh.sales            join sh.times on times.time_id = sales.time_id             join sh.customers on customers.cust_id = sales.cust_id             join sh.countries on countries.country_id = customers.country_id             join sh.products on products.prod_id = sales.prod_id         group by            prod_name , country_name , channel_id ,             substr(calendar_quarter_desc , 6 , 2 )     ))pivot(       sum(quantity_sold)    for(channel , quarter ) in    (        (5 , '02' ) as catalog_q2 ,        (4 , '01' ) as internet_q1 ,         (4 , '04' ) as internet_q4 ,        (2 , '02' ) as partners_q2 ,        (9 , '03' ) as tele_q3     ))order by product , country ;执行结果如下所示(节选)PRODUCT                                                      COUNTRY                                  CATALOG_Q2 INTERNET_Q1 INTERNET_Q4 PARTNERS_Q2    TELE_Q3------------------------------------------------------------ ---------------------------------------- ---------- ----------- ----------- ----------- ----------Model C9827B Cordless Phone Battery                          Spain                                                         6           9          25Model C9827B Cordless Phone Battery                          TurkeyModel C9827B Cordless Phone Battery                          United Kingdom                                               17          23          45Model C9827B Cordless Phone Battery                          United States of America                                    151         310         522Model CD13272 Tricolor Ink Cartridge                         ArgentinaModel CD13272 Tricolor Ink Cartridge                         Australia                                                    16          17          39Model CD13272 Tricolor Ink Cartridge                         BrazilModel CD13272 Tricolor Ink Cartridge                         Canada                                                       12          20          26Model CD13272 Tricolor Ink Cartridge                         Denmark                                                      10          15          19Model CD13272 Tricolor Ink Cartridge                         France                                                       15          14          27Model CD13272 Tricolor Ink Cartridge                         Germany                                                      28          35          64Model CD13272 Tricolor Ink Cartridge                         Italy                                                        27          23          45Model CD13272 Tricolor Ink Cartridge                         Japan                                                        24          31          73Model CD13272 Tricolor Ink Cartridge                         Singapore                                                    13          20          33Model CD13272 Tricolor Ink Cartridge                         Spain                                                        11           8          17Model CD13272 Tricolor Ink Cartridge                         TurkeyModel CD13272 Tricolor Ink Cartridge                         United Kingdom                                               16          30          53Model CD13272 Tricolor Ink Cartridge                         United States of America                                    244         314         629Model K3822L Cordless Phone Battery                          ArgentinaModel K3822L Cordless Phone Battery                          Australia                                                    19          21          49

子查询因子化的写法,共有三个因子,而且相互之间有关联

with sales_countries as (    select /*+ gather_plan_statistics */        cu.cust_id , co.country_name     from sh.countries co , sh.customers cu    where cu.country_id = co.country_id ) ,top_sales as(    select p.prod_name , sc.country_name , s.channel_id ,        t.calendar_quarter_desc , s.amount_sold , s.quantity_sold    from sh.sales s        join sh.times t on t.time_id = s.time_id         join sh.customers c on c.cust_id = s.cust_id        join sales_countries sc on sc.cust_id = c.cust_id         join sh.products p on p.prod_id = s.prod_id ) ,sales_rpt as (    select prod_name product , country_name country , channel_id channel ,            substr(calendar_quarter_desc , 6 , 2 ) quarter ,             sum(amount_sold) amount_sold , sum(quantity_sold) quantity_sold     from top_sales    group by prod_name , country_name , channel_id ,             substr(calendar_quarter_desc , 6 , 2 ))select * from (    select product , channel , quarter , country , quantity_sold     from sales_rpt)pivot(       sum(quantity_sold)    for(channel , quarter ) in    (        (5 , '02' ) as catalog_q2 ,        (4 , '01' ) as internet_q1 ,         (4 , '04' ) as internet_q4 ,        (2 , '02' ) as partners_q2 ,        (9 , '03' ) as tele_q3     ))order by product , country ;

子查询因子化所带来的好处之一
如果一个因子被多处引用,那么Oracle就会为这个因子建立临时表,免得每次都要执行。但是如果选择的不恰当,也可以极大的降低性能。

对于查询因子采用临时表的控制及各自的执行计划

用提示将查询因子物化成临时表。(不加提示时,本例也会默认采用这种办法)

explain plan for with cust as(    select /*+ materialize gather_plan_statistics */         b.cust_income_level , a.country_name     from sh.customers b    join sh.countries a on a.country_id = b.country_id )select country_name , cust_income_level , count(country_name) country_cust_count from cust chaving count(country_name) > (select count(*)*.1 from cust c2 )    or count(cust_income_level) >=     (         select median(income_level_count)            from (                select cust_income_level , count(*)*.25 income_level_count                    from cust                     group by cust_income_level                )    )group by country_name , cust_income_level order by 1 , 2 ;SH@ prod> select * from table(dbms_xplan.display()) ;PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------Plan hash value: 3111068495--------------------------------------------------------------------------------------------------------| Id  | Operation                  | Name                      | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT           |                           |    20 |   620 |   495   (1)| 00:00:06 ||   1 |  TEMP TABLE TRANSFORMATION |                           |       |       |            |          ||   2 |   LOAD AS SELECT           | SYS_TEMP_0FD9D6607_1A61BF |       |       |            |          ||*  3 |    HASH JOIN               |                           | 55500 |  2167K|   409   (1)| 00:00:05 ||   4 |     TABLE ACCESS FULL      | COUNTRIES                 |    23 |   345 |     3   (0)| 00:00:01 ||   5 |     TABLE ACCESS FULL      | CUSTOMERS                 | 55500 |  1354K|   405   (1)| 00:00:05 ||*  6 |   FILTER                   |                           |       |       |            |          ||   7 |    SORT GROUP BY           |                           |    20 |   620 |    87   (4)| 00:00:02 ||   8 |     VIEW                   |                           | 55500 |  1680K|    84   (0)| 00:00:02 ||   9 |      TABLE ACCESS FULL     | SYS_TEMP_0FD9D6607_1A61BF | 55500 |  1680K|    84   (0)| 00:00:02 ||  10 |    SORT AGGREGATE          |                           |     1 |       |            |          ||  11 |     VIEW                   |                           | 55500 |       |    84   (0)| 00:00:02 ||  12 |      TABLE ACCESS FULL     | SYS_TEMP_0FD9D6607_1A61BF | 55500 |  1680K|    84   (0)| 00:00:02 ||  13 |    SORT GROUP BY           |                           |     1 |    13 |            |          ||  14 |     VIEW                   |                           |    12 |   156 |    87   (4)| 00:00:02 ||  15 |      SORT GROUP BY         |                           |    12 |   252 |    87   (4)| 00:00:02 ||  16 |       VIEW                 |                           | 55500 |  1138K|    84   (0)| 00:00:02 ||  17 |        TABLE ACCESS FULL   | SYS_TEMP_0FD9D6607_1A61BF | 55500 |  1680K|    84   (0)| 00:00:02 |--------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------   3 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")   6 - filter(COUNT("COUNTRY_NAME")> (SELECT COUNT(*)*.1 FROM  (SELECT /*+ CACHE_TEMP_TABLE              ("T1") */ "C0" "CUST_INCOME_LEVEL","C1" "COUNTRY_NAME" FROM "SYS"."SYS_TEMP_0FD9D6607_1A61BF"              "T1") "C2") OR COUNT("CUST_INCOME_LEVEL")>= (SELECT PERCENTILE_CONT(0.500000) WITHIN GROUP (              ORDER BY "INCOME_LEVEL_COUNT") FROM  (SELECT "CUST_INCOME_LEVEL"              "CUST_INCOME_LEVEL",COUNT(*)*.25 "INCOME_LEVEL_COUNT" FROM  (SELECT /*+ CACHE_TEMP_TABLE ("T1")              */ "C0" "CUST_INCOME_LEVEL","C1" "COUNTRY_NAME" FROM "SYS"."SYS_TEMP_0FD9D6607_1A61BF" "T1")              "CUST" GROUP BY "CUST_INCOME_LEVEL") "from$_subquery$_006"))36 rows selected.

使用INLINE提示,查询因子做内联处理。

explain plan for with cust as(    select /*+ inline gather_plan_statistics */         b.cust_income_level , a.country_name     from sh.customers b    join sh.countries a on a.country_id = b.country_id )select country_name , cust_income_level , count(country_name) country_cust_count from cust chaving count(country_name) > (select count(*)*.1 from cust c2 )    or count(cust_income_level) >=     (         select median(income_level_count)            from (                select cust_income_level , count(*)*.25 income_level_count                    from cust                     group by cust_income_level                )    )group by country_name , cust_income_level order by 1 , 2 ;SH@ prod> select * from table(dbms_xplan.display()) ;PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------Plan hash value: 33565775------------------------------------------------------------------------------------------| Id  | Operation               | Name           | Rows  | Bytes | Cost (%CPU)| Time     |------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT        |                |    20 |   800 |   411   (1)| 00:00:05 ||*  1 |  FILTER                 |                |       |       |            |          ||   2 |   SORT GROUP BY         |                |    20 |   800 |   411   (1)| 00:00:05 ||*  3 |    HASH JOIN            |                | 55500 |  2167K|   409   (1)| 00:00:05 ||   4 |     TABLE ACCESS FULL   | COUNTRIES      |    23 |   345 |     3   (0)| 00:00:01 ||   5 |     TABLE ACCESS FULL   | CUSTOMERS      | 55500 |  1354K|   405   (1)| 00:00:05 ||   6 |   SORT AGGREGATE        |                |     1 |     9 |            |          ||*  7 |    HASH JOIN            |                | 55500 |   487K|    37   (3)| 00:00:01 ||   8 |     INDEX FULL SCAN     | COUNTRIES_PK   |    23 |   115 |     1   (0)| 00:00:01 ||   9 |     INDEX FAST FULL SCAN| CUST_COUNTRYID | 55500 |   216K|    35   (0)| 00:00:01 ||  10 |   SORT GROUP BY         |                |     1 |    13 |            |          ||  11 |    VIEW                 |                |    12 |   156 |   409   (1)| 00:00:05 ||  12 |     SORT GROUP BY       |                |    12 |   360 |   409   (1)| 00:00:05 ||* 13 |      HASH JOIN          |                | 55500 |  1625K|   407   (1)| 00:00:05 ||  14 |       INDEX FULL SCAN   | COUNTRIES_PK   |    23 |   115 |     1   (0)| 00:00:01 ||  15 |       TABLE ACCESS FULL | CUSTOMERS      | 55500 |  1354K|   405   (1)| 00:00:05 |------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   1 - filter(COUNT(*)> (SELECT COUNT(*)*.1 FROM "SH"."COUNTRIES"PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------              "A","SH"."CUSTOMERS" "B" WHERE "A"."COUNTRY_ID"="B"."COUNTRY_ID") OR              COUNT("B"."CUST_INCOME_LEVEL")>= (SELECT PERCENTILE_CONT(0.500000) WITHIN GROUP (              ORDER BY "INCOME_LEVEL_COUNT") FROM  (SELECT "B"."CUST_INCOME_LEVEL"              "CUST_INCOME_LEVEL",COUNT(*)*.25 "INCOME_LEVEL_COUNT" FROM "SH"."COUNTRIES"              "A","SH"."CUSTOMERS" "B" WHERE "A"."COUNTRY_ID"="B"."COUNTRY_ID" GROUP BY              "B"."CUST_INCOME_LEVEL") "from$_subquery$_006"))   3 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")   7 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")  13 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")36 rows selected.

注意在这个例子中,内联处理要快过临时表的方法。

使用临时表的性能强过内联的例子

先清空两池。SYS@ prod> alter system flush buffer_cache ;System altered.Elapsed: 00:00:00.22SYS@ prod> alter system flush shared_pool ;System altered.with cust as(    select /*+ inline gather_plan_statistics */         b.cust_income_level , a.country_name     from sh.customers b    join sh.countries a on a.country_id = b.country_id ) ,median_income_set as (    select /*+ inline */ cust_income_level , count(*) income_level_count    from cust    group by cust_income_level    having count(cust_income_level) >    (        select median(income_level_count) income_level_count         from (                select cust_income_level , count(*) income_level_count from cust                 group by cust_income_level             )    ))select country_name , cust_income_level , count(country_name) country_cust_count from cust chaving count(country_name) > (select count(*)*.1 from cust c2 )    or cust_income_level in     (        select mis.cust_income_level from median_income_set mis     )group by country_name , cust_income_level ;SH@ prod> select * from table(dbms_xplan.display()) ;PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------Plan hash value: 1450169399------------------------------------------------------------------------------------------| Id  | Operation               | Name           | Rows  | Bytes | Cost (%CPU)| Time     |------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT        |                |    20 |   800 |   411   (1)| 00:00:05 ||*  1 |  FILTER                 |                |       |       |            |          ||   2 |   HASH GROUP BY         |                |    20 |   800 |   411   (1)| 00:00:05 ||*  3 |    HASH JOIN            |                | 55500 |  2167K|   409   (1)| 00:00:05 ||   4 |     TABLE ACCESS FULL   | COUNTRIES      |    23 |   345 |     3   (0)| 00:00:01 ||   5 |     TABLE ACCESS FULL   | CUSTOMERS      | 55500 |  1354K|   405   (1)| 00:00:05 ||   6 |   SORT AGGREGATE        |                |     1 |     9 |            |          ||*  7 |    HASH JOIN            |                | 55500 |   487K|    37   (3)| 00:00:01 ||   8 |     INDEX FULL SCAN     | COUNTRIES_PK   |    23 |   115 |     1   (0)| 00:00:01 ||   9 |     INDEX FAST FULL SCAN| CUST_COUNTRYID | 55500 |   216K|    35   (0)| 00:00:01 ||* 10 |   FILTER                |                |       |       |            |          ||  11 |    HASH GROUP BY        |                |     1 |    30 |   409   (1)| 00:00:05 ||* 12 |     HASH JOIN           |                | 55500 |  1625K|   407   (1)| 00:00:05 ||  13 |      INDEX FULL SCAN    | COUNTRIES_PK   |    23 |   115 |     1   (0)| 00:00:01 ||  14 |      TABLE ACCESS FULL  | CUSTOMERS      | 55500 |  1354K|   405   (1)| 00:00:05 ||  15 |    SORT GROUP BY        |                |     1 |    13 |            |          ||  16 |     VIEW                |                |    12 |   156 |   409   (1)| 00:00:05 ||  17 |      SORT GROUP BY      |                |    12 |   360 |   409   (1)| 00:00:05 ||* 18 |       HASH JOIN         |                | 55500 |  1625K|   407   (1)| 00:00:05 ||  19 |        INDEX FULL SCAN  | COUNTRIES_PK   |    23 |   115 |     1   (0)| 00:00:01 ||  20 |        TABLE ACCESS FULL| CUSTOMERS      | 55500 |  1354K|   405   (1)| 00:00:05 |------------------------------------------------------------------------------------------PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   1 - filter(COUNT(*)> (SELECT COUNT(*)*.1 FROM "SH"."COUNTRIES"              "A","SH"."CUSTOMERS" "B" WHERE "A"."COUNTRY_ID"="B"."COUNTRY_ID") OR  EXISTS              (SELECT 0 FROM "SH"."COUNTRIES" "A","SH"."CUSTOMERS" "B" WHERE              "A"."COUNTRY_ID"="B"."COUNTRY_ID" GROUP BY "B"."CUST_INCOME_LEVEL" HAVING              "B"."CUST_INCOME_LEVEL"=:B1 AND COUNT("B"."CUST_INCOME_LEVEL")> (SELECT              PERCENTILE_CONT(0.500000) WITHIN GROUP ( ORDER BY "INCOME_LEVEL_COUNT") FROM              (SELECT "B"."CUST_INCOME_LEVEL" "CUST_INCOME_LEVEL",COUNT(*) "INCOME_LEVEL_COUNT"              FROM "SH"."COUNTRIES" "A","SH"."CUSTOMERS" "B" WHERE              "A"."COUNTRY_ID"="B"."COUNTRY_ID" GROUP BY "B"."CUST_INCOME_LEVEL")              "from$_subquery$_005")))   3 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")   7 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")  10 - filter("B"."CUST_INCOME_LEVEL"=:B1 AND COUNT("B"."CUST_INCOME_LEVEL")>              (SELECT PERCENTILE_CONT(0.500000) WITHIN GROUP ( ORDER BY "INCOME_LEVEL_COUNT")              FROM  (SELECT "B"."CUST_INCOME_LEVEL" "CUST_INCOME_LEVEL",COUNT(*)              "INCOME_LEVEL_COUNT" FROM "SH"."COUNTRIES" "A","SH"."CUSTOMERS" "B" WHERE              "A"."COUNTRY_ID"="B"."COUNTRY_ID" GROUP BY "B"."CUST_INCOME_LEVEL")              "from$_subquery$_005"))  12 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")  18 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")执行结果COUNTRY_NAME                             CUST_INCOME_LEVEL              COUNTRY_CUST_COUNT---------------------------------------- ------------------------------ ------------------China                                    F: 110,000 - 129,999                          181Poland                                   H: 150,000 - 169,999                           61Singapore                                H: 150,000 - 169,999                           50New Zealand                              H: 150,000 - 169,999                           21Brazil                                   E: 90,000 - 109,999                           105Denmark                                  E: 90,000 - 109,999                            61114 rows selected.Elapsed: 00:00:00.51使用临时表。with cust as(    select /*+ materialize gather_plan_statistics */         b.cust_income_level , a.country_name     from sh.customers b    join sh.countries a on a.country_id = b.country_id ) ,median_income_set as (    select /*+ inline */ cust_income_level , count(*) income_level_count    from cust    group by cust_income_level    having count(cust_income_level) >    (        select median(income_level_count) income_level_count         from (                select cust_income_level , count(*) income_level_count from cust                 group by cust_income_level             )    ))select country_name , cust_income_level , count(country_name) country_cust_count from cust chaving count(country_name) > (select count(*)*.1 from cust c2 )    or cust_income_level in     (        select mis.cust_income_level from median_income_set mis     )group by country_name , cust_income_level ;SH@ prod> select * from table(dbms_xplan.display());PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------------------------------Plan hash value: 663917268--------------------------------------------------------------------------------------------------------| Id  | Operation                  | Name                      | Rows  | Bytes | Cost (%CPU)| Time     |--------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT           |                           |    20 |   620 |   495   (1)| 00:00:06 ||   1 |  TEMP TABLE TRANSFORMATION |                           |       |       |            |          ||   2 |   LOAD AS SELECT           | SYS_TEMP_0FD9D660D_1A61BF |       |       |            |          ||*  3 |    HASH JOIN               |                           | 55500 |  2167K|   409   (1)| 00:00:05 ||   4 |     TABLE ACCESS FULL      | COUNTRIES                 |    23 |   345 |     3   (0)| 00:00:01 ||   5 |     TABLE ACCESS FULL      | CUSTOMERS                 | 55500 |  1354K|   405   (1)| 00:00:05 ||*  6 |   FILTER                   |                           |       |       |            |          ||   7 |    HASH GROUP BY           |                           |    20 |   620 |    87   (4)| 00:00:02 ||   8 |     VIEW                   |                           | 55500 |  1680K|    84   (0)| 00:00:02 ||   9 |      TABLE ACCESS FULL     | SYS_TEMP_0FD9D660D_1A61BF | 55500 |  1680K|    84   (0)| 00:00:02 ||  10 |    SORT AGGREGATE          |                           |     1 |       |            |          ||  11 |     VIEW                   |                           | 55500 |       |    84   (0)| 00:00:02 ||  12 |      TABLE ACCESS FULL     | SYS_TEMP_0FD9D660D_1A61BF | 55500 |  1680K|    84   (0)| 00:00:02 ||* 13 |    FILTER                  |                           |       |       |            |          ||  14 |     HASH GROUP BY          |                           |     1 |    21 |    87   (4)| 00:00:02 ||  15 |      VIEW                  |                           | 55500 |  1138K|    84   (0)| 00:00:02 ||  16 |       TABLE ACCESS FULL    | SYS_TEMP_0FD9D660D_1A61BF | 55500 |  1680K|    84   (0)| 00:00:02 ||  17 |     SORT GROUP BY          |                           |     1 |    13 |            |          ||  18 |      VIEW                  |                           |    12 |   156 |    87   (4)| 00:00:02 ||  19 |       SORT GROUP BY        |                           |    12 |   252 |    87   (4)| 00:00:02 ||  20 |        VIEW                |                           | 55500 |  1138K|    84   (0)| 00:00:02 ||  21 |         TABLE ACCESS FULL  | SYS_TEMP_0FD9D660D_1A61BF | 55500 |  1680K|    84   (0)| 00:00:02 |PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   3 - access("A"."COUNTRY_ID"="B"."COUNTRY_ID")   6 - filter(COUNT("COUNTRY_NAME")> (SELECT COUNT(*)*.1 FROM  (SELECT /*+ CACHE_TEMP_TABLE              ("T1") */ "C0" "CUST_INCOME_LEVEL","C1" "COUNTRY_NAME" FROM "SYS"."SYS_TEMP_0FD9D660D_1A61BF"              "T1") "C2") OR  EXISTS (SELECT 0 FROM  (SELECT /*+ CACHE_TEMP_TABLE ("T1") */ "C0"              "CUST_INCOME_LEVEL","C1" "COUNTRY_NAME" FROM "SYS"."SYS_TEMP_0FD9D660D_1A61BF" "T1") "CUST"              GROUP BY "CUST_INCOME_LEVEL" HAVING "CUST_INCOME_LEVEL"=:B1 AND COUNT("CUST_INCOME_LEVEL")>              (SELECT PERCENTILE_CONT(0.500000) WITHIN GROUP ( ORDER BY "INCOME_LEVEL_COUNT") FROM  (SELECT              "CUST_INCOME_LEVEL" "CUST_INCOME_LEVEL",COUNT(*) "INCOME_LEVEL_COUNT" FROM  (SELECT /*+              CACHE_TEMP_TABLE ("T1") */ "C0" "CUST_INCOME_LEVEL","C1" "COUNTRY_NAME" FROM              "SYS"."SYS_TEMP_0FD9D660D_1A61BF" "T1") "CUST" GROUP BY "CUST_INCOME_LEVEL")              "from$_subquery$_005")))  13 - filter("CUST_INCOME_LEVEL"=:B1 AND COUNT("CUST_INCOME_LEVEL")> (SELECT              PERCENTILE_CONT(0.500000) WITHIN GROUP ( ORDER BY "INCOME_LEVEL_COUNT") FROM  (SELECT              "CUST_INCOME_LEVEL" "CUST_INCOME_LEVEL",COUNT(*) "INCOME_LEVEL_COUNT" FROM  (SELECT /*+              CACHE_TEMP_TABLE ("T1") */ "C0" "CUST_INCOME_LEVEL","C1" "COUNTRY_NAME" FROM              "SYS"."SYS_TEMP_0FD9D660D_1A61BF" "T1") "CUST" GROUP BY "CUST_INCOME_LEVEL")              "from$_subquery$_005"))49 rows selected.COUNTRY_NAME                             CUST_INCOME_LEVEL              COUNTRY_CUST_COUNT---------------------------------------- ------------------------------ ------------------China                                    F: 110,000 - 129,999                          181Poland                                   H: 150,000 - 169,999                           61Singapore                                H: 150,000 - 169,999                           50New Zealand                              H: 150,000 - 169,999                           21Brazil                                   E: 90,000 - 109,999                           105Denmark                                  E: 90,000 - 109,999                            61114 rows selected.Elapsed: 00:00:00.32

用因子化优化SQL(*)

存在这样一条老SQL

select /*+ gather_plan_statistics */    substr(prod_name , 1 , 30 ) prod_name , channel_desc ,    (         select avg(c2.unit_cost)        from sh.costs c2         where c2.prod_id = c.prod_id and c2.channel_id = c.channel_id         and c2.time_id between to_date('01/01/2000' , 'mm/dd/yyyy' )                            and to_date('12/31/2000' , 'mm/dd/yyyy')     ) avg_cost ,    (        select min(c2.unit_cost)        from sh.costs c2        where c2.prod_id = c.prod_id and c2.channel_id = c.channel_id         and c2.time_id between to_date('01/01/2000' , 'mm/dd/yyyy' )                            and to_date('12/31/2000' , 'mm/dd/yyyy')     ) min_cost ,    (        select max(c2.unit_cost)        from sh.costs c2        where c2.prod_id = c.prod_id and c2.channel_id = c.channel_id         and c2.time_id between to_date('01/01/2000' , 'mm/dd/yyyy' )                            and to_date('12/31/2000' , 'mm/dd/yyyy')     ) max_cost from (    select distinct pr.prod_id , pr.prod_name , ch.channel_id , ch.channel_desc     from sh.channels ch , sh.products pr , sh.costs co    where ch.channel_id = co.channel_id     and co.prod_id = pr.prod_id     and co.time_id between to_date('01/01/2000' , 'mm/dd/yyyy')                        and to_date('12/31/2000' , 'mm/dd/yyyy')) corder by prod_name , channel_desc ;执行时间Elapsed: 00:00:00.36执行计划SH@ prod> select * from table(dbms_xplan.display()) ;PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Plan hash value: 1877279774------------------------------------------------------------------------------------------------------------------------------| Id  | Operation                           | Name           | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |------------------------------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT                    |                | 20640 |  1310K|       |   638   (1)| 00:00:08 |       |       ||   1 |  SORT AGGREGATE                     |                |     1 |    20 |       |            |          |       |       ||   2 |   PARTITION RANGE ITERATOR          |                |    96 |  1920 |       |    17   (0)| 00:00:01 |    13 |    16 ||*  3 |    TABLE ACCESS BY LOCAL INDEX ROWID| COSTS          |    96 |  1920 |       |    17   (0)| 00:00:01 |    13 |    16 ||   4 |     BITMAP CONVERSION TO ROWIDS     |                |       |       |       |            |          |       |       ||*  5 |      BITMAP INDEX SINGLE VALUE      | COSTS_PROD_BIX |       |       |       |            |          |    13 |    16 ||   6 |  SORT AGGREGATE                     |                |     1 |    20 |       |            |          |       |       ||   7 |   PARTITION RANGE ITERATOR          |                |    96 |  1920 |       |    17   (0)| 00:00:01 |    13 |    16 ||*  8 |    TABLE ACCESS BY LOCAL INDEX ROWID| COSTS          |    96 |  1920 |       |    17   (0)| 00:00:01 |    13 |    16 ||   9 |     BITMAP CONVERSION TO ROWIDS     |                |       |       |       |            |          |       |       ||* 10 |      BITMAP INDEX SINGLE VALUE      | COSTS_PROD_BIX |       |       |       |            |          |    13 |    16 ||  11 |  SORT AGGREGATE                     |                |     1 |    20 |       |            |          |       |       ||  12 |   PARTITION RANGE ITERATOR          |                |    96 |  1920 |       |    17   (0)| 00:00:01 |    13 |    16 ||* 13 |    TABLE ACCESS BY LOCAL INDEX ROWID| COSTS          |    96 |  1920 |       |    17   (0)| 00:00:01 |    13 |    16 ||  14 |     BITMAP CONVERSION TO ROWIDS     |                |       |       |       |            |          |       |       ||* 15 |      BITMAP INDEX SINGLE VALUE      | COSTS_PROD_BIX |       |       |       |            |          |    13 |    16 ||  16 |  SORT ORDER BY                      |                | 20640 |  1310K|  1632K|   638   (1)| 00:00:08 |       |       ||  17 |   VIEW                              |                | 20640 |  1310K|       |   315   (1)| 00:00:04 |       |       ||  18 |    HASH UNIQUE                      |                | 20640 |  1169K|  1384K|   315   (1)| 00:00:04 |       |       ||* 19 |     HASH JOIN                       |                | 20640 |  1169K|       |    24   (5)| 00:00:01 |       |       ||  20 |      TABLE ACCESS FULL              | PRODUCTS       |    72 |  2160 |       |     3   (0)| 00:00:01 |       |       ||* 21 |      HASH JOIN                      |                | 20640 |   564K|       |    21   (5)| 00:00:01 |       |       |PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|  22 |       TABLE ACCESS FULL             | CHANNELS       |     5 |    65 |       |     3   (0)| 00:00:01 |       |       ||  23 |       PARTITION RANGE ITERATOR      |                | 20640 |   302K|       |    17   (0)| 00:00:01 |    13 |    16 ||* 24 |        TABLE ACCESS FULL            | COSTS          | 20640 |   302K|       |    17   (0)| 00:00:01 |    13 |    16 |------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   3 - filter("C2"."CHANNEL_ID"=:B1 AND "C2"."TIME_ID"<=TO_DATE(' 2000-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))   5 - access("C2"."PROD_ID"=:B1)   8 - filter("C2"."CHANNEL_ID"=:B1 AND "C2"."TIME_ID"<=TO_DATE(' 2000-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))  10 - access("C2"."PROD_ID"=:B1)  13 - filter("C2"."CHANNEL_ID"=:B1 AND "C2"."TIME_ID"<=TO_DATE(' 2000-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))  15 - access("C2"."PROD_ID"=:B1)  19 - access("CO"."PROD_ID"="PR"."PROD_ID")  21 - access("CH"."CHANNEL_ID"="CO"."CHANNEL_ID")  24 - filter("CO"."TIME_ID"<=TO_DATE(' 2000-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))44 rows selected.

用使WITH子句进行重构

with bookends as (    select to_date('01/01/2000' , 'mm/dd/yyyy' ) begin_date ,        to_date('12/31/2000' , 'mm/dd/yyyy') end_date         from dual ) ,prodmaster as (    select distinct pr.prod_id , pr.prod_name , ch.channel_id , ch.channel_desc     from sh.channels ch , sh.products pr , sh.costs co    where ch.channel_id = co.channel_id     and co.prod_id = pr.prod_id     and co.time_id between (select begin_date from bookends)                        and (select end_date from bookends)) ,cost_compare as (    select prod_id , channel_id , avg(c2.unit_cost) avg_cost ,         min(c2.unit_cost) min_cost , max(c2.unit_cost) max_cost     from sh.costs c2    where c2.time_id between ( select begin_date from bookends )                        and ( select end_date from bookends )    group by c2.prod_id , c2.channel_id )select /*+ gather_plan_statistics */     substr(pm.prod_name , 1 , 30) prod_name , pm.channel_desc ,     cc.avg_cost , cc.min_cost , cc.max_cost from prodmaster pmjoin cost_compare cc on cc.prod_id = pm.prod_id     and cc.channel_id = pm.channel_id order by pm.prod_name , pm.channel_desc ;新语句的执行时间Elapsed: 00:00:00.14 (是原来的三分之一)新语句的执行计划SH@ prod> select * from table(dbms_xplan.display()) ;PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Plan hash value: 134863587----------------------------------------------------------------------------------------------------------------------------| Id  | Operation                                 | Name           | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |----------------------------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT                          |                |   138 | 12696 |    84   (6)| 00:00:02 |       |       ||   1 |  SORT ORDER BY                            |                |   138 | 12696 |    84   (6)| 00:00:02 |       |       ||*  2 |   HASH JOIN                               |                |   138 | 12696 |    83   (5)| 00:00:01 |       |       ||   3 |    VIEW                                   |                |   145 |  6670 |    38   (3)| 00:00:01 |       |       ||   4 |     HASH GROUP BY                         |                |   145 |  2900 |    38   (3)| 00:00:01 |       |       ||   5 |      PARTITION RANGE ITERATOR             |                |   205 |  4100 |    33   (0)| 00:00:01 |   KEY |   KEY ||   6 |       TABLE ACCESS BY LOCAL INDEX ROWID   | COSTS          |   205 |  4100 |    33   (0)| 00:00:01 |   KEY |   KEY ||   7 |        BITMAP CONVERSION TO ROWIDS        |                |       |       |            |          |       |       ||*  8 |         BITMAP INDEX RANGE SCAN           | COSTS_TIME_BIX |       |       |            |          |   KEY |   KEY ||   9 |          FAST DUAL                        |                |     1 |       |     2   (0)| 00:00:01 |       |       ||  10 |          FAST DUAL                        |                |     1 |       |     2   (0)| 00:00:01 |       |       ||  11 |    VIEW                                   |                |   205 |  9430 |    44   (5)| 00:00:01 |       |       ||  12 |     HASH UNIQUE                           |                |   205 | 11890 |    44   (5)| 00:00:01 |       |       ||* 13 |      HASH JOIN                            |                |   205 | 11890 |    39   (3)| 00:00:01 |       |       ||  14 |       TABLE ACCESS FULL                   | PRODUCTS       |    72 |  2160 |     3   (0)| 00:00:01 |       |       ||  15 |       MERGE JOIN                          |                |   205 |  5740 |    36   (3)| 00:00:01 |       |       ||  16 |        TABLE ACCESS BY INDEX ROWID        | CHANNELS       |     5 |    65 |     2   (0)| 00:00:01 |       |       ||  17 |         INDEX FULL SCAN                   | CHANNELS_PK    |     5 |       |     1   (0)| 00:00:01 |       |       ||* 18 |        SORT JOIN                          |                |   205 |  3075 |    34   (3)| 00:00:01 |       |       ||  19 |         PARTITION RANGE ITERATOR          |                |   205 |  3075 |    33   (0)| 00:00:01 |   KEY |   KEY ||  20 |          TABLE ACCESS BY LOCAL INDEX ROWID| COSTS          |   205 |  3075 |    33   (0)| 00:00:01 |   KEY |   KEY ||  21 |           BITMAP CONVERSION TO ROWIDS     |                |       |       |            |          |       |       |PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|* 22 |            BITMAP INDEX RANGE SCAN        | COSTS_TIME_BIX |       |       |            |          |   KEY |   KEY ||  23 |             FAST DUAL                     |                |     1 |       |     2   (0)| 00:00:01 |       |       ||  24 |             FAST DUAL                     |                |     1 |       |     2   (0)| 00:00:01 |       |       |----------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   2 - access("CC"."PROD_ID"="PM"."PROD_ID" AND "CC"."CHANNEL_ID"="PM"."CHANNEL_ID")   8 - access("C2"."TIME_ID">= (SELECT TO_DATE(' 2000-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') FROM "SYS"."DUAL"              "DUAL") AND "C2"."TIME_ID"<= (SELECT TO_DATE(' 2000-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss') FROM "SYS"."DUAL"              "DUAL"))  13 - access("CO"."PROD_ID"="PR"."PROD_ID")  18 - access("CH"."CHANNEL_ID"="CO"."CHANNEL_ID")       filter("CH"."CHANNEL_ID"="CO"."CHANNEL_ID")  22 - access("CO"."TIME_ID">= (SELECT TO_DATE(' 2000-01-01 00:00:00', 'syyyy-mm-dd hh24:mi:ss') FROM "SYS"."DUAL"              "DUAL") AND "CO"."TIME_ID"<= (SELECT TO_DATE(' 2000-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss') FROM "SYS"."DUAL"              "DUAL"))45 rows selected.

用复杂查询来代替PL/SQL程序

下面的PL/SQL块查询出来了3年以上的顾客的信息,并将其插入全局临时表中。

begin    execute immediate 'truncate table cust3year' ;    execute immediate 'truncate table sales3year' ;    insert into cust3year        select cust_id -- , count(cust_years) year_count        from (            select distinct cust_id , trunc(time_id , 'year') cust_years             from sh.sales         )        group by cust_id        having count(cust_years) >= 3 ;        for crec in (select cust_id from cust3year)        loop            insert into sales3year                select s.cust_id , p.prod_category , sum(co.unit_price*s.quantity_sold)                from sh.sales s                join sh.products p on p.prod_id = s.prod_id                join sh.costs co on co.prod_id = s.prod_id                                     and co.time_id = s.time_id                 join sh.customers cu on cu.cust_id = s.cust_id                 where s.cust_id = crec.cust_id                 group by s.cust_id , p.prod_category ;        end loop ;end ;执行情况PL/SQL procedure successfully completed.Elapsed: 00:00:54.86查看结果SH@ prod> break on report SH@ prod> compute sum of total_sale on report SH@ prod> select c3.cust_id , c.cust_last_name , c.cust_first_name , s.prod_category , s.total_sale  2  from cust3year c3   3  join sales3year s on s.cust_id = c3.cust_id   4  join sh.customers c on c.cust_id = c3.cust_id   5  order by 1 , 4 ;

用因子化的查询以及分析函数来完成上面这件事。
采用了因子间的嵌套,如果不使用因子很难完成。

with custyear as(    select cust_id , extract(year from time_id) sales_year     from sh.sales    where extract(year from time_id ) between 1998 and 2002    group by cust_id , extract(year from time_id)) ,custselect as (    select distinct cust_id     from (        select cust_id , count(*) over(partition by cust_id) year_count        from custyear    )    where year_count >= 3 )select cu.cust_id , cu.cust_last_name , cu.cust_first_name , p.prod_category ,    sum(co.unit_price * s.quantity_sold) total_sale from custselect csjoin sh.sales s on s.cust_id = cs.cust_idjoin sh.products p on p.prod_id = s.prod_idjoin sh.costs co on co.prod_id = s.prod_id     and co.time_id = s.time_id join sh.customers cu on cu.cust_id = cs.cust_id group by cu.cust_id , cu.cust_last_name , cu.cust_first_name , p.prod_category order by cu.cust_id ;执行结果16018 rows selected.Elapsed: 00:00:07.66

RSF递归子查询因子化(11.2中才出现)

对应ANSI中的recursive common table expression。

RSF与CONNECT BY

用CONNECT BY

HR@ prod> set linesize 180select lpad(' ' , level*2 - 1 , ' ' ) || emp.emp_last_name emp_last_name ,    emp.emp_first_name , emp.employee_id , emp.mgr_last_name , emp.mgr_first_name ,     emp.manager_id , department_name from (    select /*+ inline gather plan statistics */    e.last_name emp_last_name , e.first_name emp_first_name ,     e.employee_id , d.department_id , e.manager_id , d.department_name ,    es.last_name mgr_last_name , es.first_name mgr_first_name     from hr.employees e    left outer join hr.departments d on d.department_id = e.department_id    left outer join hr.employees es on es.employee_id = e.manager_id     ) empconnect by prior emp.employee_id = emp.manager_id start with emp.manager_id is nullorder siblings by emp.emp_last_name ;EMP_LAST_NAME                  EMP_FIRST_NAME       EMPLOYEE_ID MGR_LAST_NAME             MGR_FIRST_NAME       MANAGER_ID DEPARTMENT_NAME------------------------------ -------------------- ----------- ------------------------- -------------------- ---------- ------------------------------ King                          Steven                       100                                                           Executive   Cambrault                   Gerald                       148 King                      Steven                      100 Sales     Bates                     Elizabeth                    172 Cambrault                 Gerald                      148 Sales     Bloom                     Harrison                     169 Cambrault                 Gerald                      148 Sales     Fox                       Tayler                       170 Cambrault                 Gerald                      148 Sales     Kumar                     Sundita                      173 Cambrault                 Gerald                      148 Sales     Ozer                      Lisa                         168 Cambrault                 Gerald                      148 Sales     Smith                     William                      171 Cambrault                 Gerald                      148 Sales   De Haan                     Lex                          102 King                      Steven                      100 Executive     Hunold                    Alexander                    103 De Haan                   Lex                         102 IT       Austin                  David                        105 Hunold                    Alexander                   103 IT       Ernst                   Bruce                        104 Hunold                    Alexander                   103 IT       Lorentz                 Diana                        107 Hunold                    Alexander                   103 IT       Pataballa               Valli                        106 Hunold                    Alexander                   103 IT   Errazuriz                   Alberto                      147 King                      Steven                      100 Sales     Ande                      Sundar                       166 Errazuriz                 Alberto                     147 Sales     Banda                     Amit                         167 Errazuriz                 Alberto                     147 Sales     Greene                    Danielle                     163 Errazuriz                 Alberto                     147 Sales     Lee                       David                        165 Errazuriz                 Alberto                     147 Sales     Marvins                   Mattea                       164 Errazuriz                 Alberto                     147 Sales     Vishney                   Clara                        162 Errazuriz                 Alberto                     147 Sales   Fripp                       Adam                         121 King                      Steven                      100 Shipping     Atkinson                  Mozhe                        130 Fripp                     Adam                        121 Shipping     Bissot                    Laura                        129 Fripp                     Adam                        121 Shipping     Bull                      Alexis                       185 Fripp                     Adam                        121 Shipping     Cabrio                    Anthony                      187 Fripp                     Adam                        121 Shipping     Dellinger                 Julia                        186 Fripp                     Adam                        121 Shipping

用RSF

with emp as (    select /*+ inline gather_plan_statistics */        e.last_name , e.first_name , e.employee_id , e.manager_id , d.department_name     from hr.employees e    left outer join hr.departments d on d.department_id = e.department_id ) ,emp_recurse(last_name , first_name , employee_id , manager_id , department_name , lv1) as(    select e.last_name , e.first_name , e.employee_id , e.manager_id , e.department_name , 1 as lv1     from emp e where e.manager_id is null    union all    select emp.last_name , emp.first_name , emp.employee_id , emp.manager_id ,        emp.department_name , empr.lv1 + 1 as lv1        from emp join emp_recurse empr on empr.employee_id = emp.manager_id )search depth first by last_name set order1select lpad(' ' , lv1*2 - 1 , ' ' ) || er.last_name last_name , er.first_name , er.department_name from emp_recurse er ;LAST_NAME                      FIRST_NAME           DEPARTMENT_NAME------------------------------ -------------------- ------------------------------ King                          Steven               Executive   Cambrault                   Gerald               Sales     Bates                     Elizabeth            Sales     Bloom                     Harrison             Sales     Fox                       Tayler               Sales     Kumar                     Sundita              Sales     Ozer                      Lisa                 Sales     Smith                     William              Sales   De Haan                     Lex                  Executive     Hunold                    Alexander            IT       Austin                  David                IT       Ernst                   Bruce                IT       Lorentz                 Diana                IT       Pataballa               Valli                IT   Errazuriz                   Alberto              Sales     Ande                      Sundar               Sales     Banda                     Amit                 Sales     Greene                    Danielle             Sales     Lee                       David                Sales     Marvins                   Mattea               Sales     Vishney                   Clara                Sales   Fripp                       Adam                 Shipping     Atkinson                  Mozhe                Shipping     Bissot                    Laura                Shipping     Bull                      Alexis               Shipping     Cabrio                    Anthony              Shipping     Dellinger                 Julia                Shipping
原创粉丝点击