Is "UNION ALL" Always Better Than "UNION"? Watch Out!

无论是教科书还是平常的实践都告诉我们 - “尽量避免用UNION,尽可能用UNION ALL替代”。 原因很简单,UNION会对结果集进行排序去重操作,这是一个很消耗资源的操作。

但是,今天碰到了一个SQL (on 11.2.0.3),如果用UNION的话,查询很快就返回结果,逻辑读不是很大。但是如果用UNION ALL的话,查询要很久才有返回,逻辑读非常大。

SQL如下(注意高亮显示的UNION ALL)

WITH v_tmp_entity_code AS
(
    SELECT CHILD_CODE
    FROM
    ( 
        SELECT     child_code,
                mother_code
        FROM     lo_entity_links_temp
        WHERE     process_id = '11838873'
        AND     temp_status <> 'DELETED'
        AND     purpose      = 'Credit Hierarchy'
        AND     TYPE         = 'Customer Grouping'
      UNION ALL
        SELECT     child_code,
                mother_code
        FROM     lo_entity_links l
        WHERE     purpose = 'Credit Hierarchy'
        AND     TYPE    = 'Customer Grouping'
        AND NOT EXISTS
        (
            SELECT     NULL
            FROM     lo_entity_links_temp t
            WHERE     l.id       = t.id
            AND     t.process_id = '11838873'
        )
    )
    START WITH MOTHER_CODE      = 568
    CONNECT BY PRIOR child_code = MOTHER_CODE
)
SELECT     UP_SUB.ENTITY_CODE AS "ENTITY_CODE",
        UP_SUB.ENTITY_NAME      AS "ENTITY_NAME"
FROM
  (
  SELECT      EN.ENTITY_CODE,
            DECODE(TA.ATTRIBUTE_6, NULL, EN.ATTRIBUTE_6, TA.ATTRIBUTE_6)                                              AS "ATTRIBUTE_6",
            DECODE(TA.incorporation_country_code, NULL, EN.incorporation_country_code, TA.incorporation_country_code) AS "INCORP_COUNTRY",
            DECODE(TA.LONG_NAME, NULL, EN.LONG_NAME, TA.LONG_NAME)                                                    AS "ENTITY_NAME",
            DECODE( TA.IS_BLACKBOOK, 'T', 1, 0) IS_BLACKBOOK_CP
  FROM         LO_ENTITY EN
  LEFT OUTER JOIN CD_USERS U  ON EN.ATTRIBUTE_7 = U.USER_NAME
  LEFT OUTER JOIN LO_ENTITY_TEMP TA ON EN.ENTITY_CODE   = TA.ENTITY_CODE
                                      AND TA.process_id   = '11838873'
                                      AND TA.TEMP_STATUS <> 'ORIGIN'
                                      AND TA.TEMP_STATUS <> 'DELETED'
                                      AND TA.process_type = 'WORKFLOW'
  WHERE  EN.id IN
    (
        SELECT child_code FROM v_tmp_entity_code
    )
  AND EN.entity_code NOT IN
    (
        SELECT     SUB_ECO.ENTITY_CODE
        FROM     V_TEMP_ANZ_CMF SUB_ECO
        WHERE     SUB_ECO.is_temp    = 'Y'
        AND     SUB_ECO.process_id   = '11838873'
        AND     SUB_ECO.ENTITY_CODE IN
        (
            SELECT TO_CHAR(child_code) FROM v_tmp_entity_code
      )
    )
  ORDER BY EN.LONG_NAME
  ) UP_SUB
UNION ALL
SELECT 
        SUPER.ENTITY_CODE AS "ENTITY_CODE",
        SUPER.ENTITY_NAME      AS "ENTITY_NAME"
FROM
(
    SELECT     UP_SUB.ENTITY_CODE,
            UP_SUB.COUNTERPARTY_NAME AS "ENTITY_NAME"
    FROM
    (
        SELECT SUB.*
        FROM
        (
            SELECT     DISTINCT 
                    DECODE(TENT.LONG_NAME, NULL, ENT.LONG_NAME, TENT.LONG_NAME) AS "COUNTERPARTY_NAME" ,
                    ENT.ENTITY_CODE,
                    CMF.CMF_ROLE                                                      AS "ROLE",
                    ENT.ATTRIBUTE_7
            FROM     V_TEMP_ANZ_CMF CMF
            LEFT OUTER JOIN LO_ENTITY_TEMP TENT
                ON CMF.ENTITY_CODE    = TENT.ENTITY_CODE
                AND TENT.process_id   = '11838873'
                AND TENT.TEMP_STATUS <> 'ORIGIN'
                AND TENT.TEMP_STATUS <> 'DELETED'
            LEFT OUTER JOIN LO_ENTITY ENT
                ON CMF.ENTITY_CODE = ENT.ENTITY_CODE
            LEFT OUTER JOIN LO_ENTITY_TEMP JTEMP
                ON JTEMP.ENTITY_CODE   = CMF.GRP
                AND JTEMP.process_id   = '11838873'
                AND JTEMP.TEMP_STATUS <> 'ORIGIN'
                AND JTEMP.TEMP_STATUS <> 'DELETED'
            LEFT OUTER JOIN LO_ENTITY JENT
                ON JENT.ENTITY_CODE  = CMF.GRP
            WHERE      CMF.is_temp      = 'Y'
            AND     CMF.process_id   = '11838873'
            AND     CMF.entity_code IN
            (
                SELECT TO_CHAR(child_code) FROM v_tmp_entity_code
            )
        ) SUB
        LEFT OUTER JOIN CD_USERS U
        ON SUB.ATTRIBUTE_7 = U.USER_NAME
        ORDER BY 
            DECODE(SUB.ROLE, 'GRP', 1, 'GRP/CP', 2, 'CP', 3, 'AP', 4),
            SUB.ENTITY_CODE
    )UP_SUB
 ) SUPER
 ;
 

 

执行计划和统计信息如下,

Elapsed: 00:00:40.50

Execution Plan
----------------------------------------------------------
Plan hash value: 495744135

--------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name                        | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                             |     3 |   387 |  1791  (67)| 00:00:22 |
|   1 |  TEMP TABLE TRANSFORMATION           |                             |       |       |         |     |
|   2 |   LOAD AS SELECT                     | SYS_TEMP_0FD9D6FDF_24D7DA10 |       |       |         |     |
|*  3 |    CONNECT BY WITH FILTERING         |                             |       |       |         |     |
|   4 |     VIEW                             |                             |     2 |    52 |     8   (0)| 00:00:01 |
|   5 |      UNION-ALL                       |                             |       |       |         |     |
|*  6 |       TABLE ACCESS BY INDEX ROWID    | LO_ENTITY_LINKS_TEMP        |     1 |    52 |     3   (0)| 00:00:01 |
|*  7 |        INDEX RANGE SCAN              | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|   8 |       NESTED LOOPS ANTI              |                             |     1 |    57 |     5   (0)| 00:00:01 |
|*  9 |        TABLE ACCESS BY INDEX ROWID   | LO_ENTITY_LINKS             |     1 |    45 |     2   (0)| 00:00:01 |
|* 10 |         INDEX RANGE SCAN             | I1_LO_ENTITY_LINKS          |     1 |       |     1   (0)| 00:00:01 |
|* 11 |        TABLE ACCESS BY INDEX ROWID   | LO_ENTITY_LINKS_TEMP        |     1 |    12 |     3   (0)| 00:00:01 |
|* 12 |         INDEX RANGE SCAN             | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|  13 |     VIEW                             |                             |     3 |    78 |    27   (4)| 00:00:01 |
|  14 |      UNION-ALL                       |                             |       |       |         |     |
|* 15 |       HASH JOIN                      |                             |     1 |    65 |    12   (9)| 00:00:01 |
|* 16 |        TABLE ACCESS BY INDEX ROWID   | LO_ENTITY_LINKS_TEMP        |     1 |    52 |     3   (0)| 00:00:01 |
|* 17 |         INDEX RANGE SCAN             | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|  18 |        CONNECT BY PUMP               |                             |       |       |         |     |
|* 19 |       HASH JOIN ANTI                 |                             |     2 |   140 |    16   (7)| 00:00:01 |
|  20 |        NESTED LOOPS                  |                             |       |       |         |     |
|  21 |         NESTED LOOPS                 |                             |     2 |   116 |    12   (0)| 00:00:01 |
|  22 |          CONNECT BY PUMP             |                             |       |       |         |     |
|* 23 |          INDEX RANGE SCAN            | I1_LO_ENTITY_LINKS          |     1 |       |     1   (0)| 00:00:01 |
|* 24 |         TABLE ACCESS BY INDEX ROWID  | LO_ENTITY_LINKS             |     1 |    45 |     2   (0)| 00:00:01 |
|  25 |        TABLE ACCESS BY INDEX ROWID   | LO_ENTITY_LINKS_TEMP        |     1 |    12 |     3   (0)| 00:00:01 |
|* 26 |         INDEX RANGE SCAN             | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|  27 |   UNION-ALL                          |                             |       |       |         |     |
|  28 |    VIEW                              |                             |     1 |   129 |   602   (1)| 00:00:08 |
|  29 |     SORT ORDER BY                    |                             |     1 |   105 |   602   (1)| 00:00:08 |
|* 30 |      HASH JOIN ANTI                  |                             |     1 |   105 |   601   (1)| 00:00:08 |
|* 31 |       HASH JOIN OUTER                |                             |     1 |    97 |   587   (1)| 00:00:08 |
|  32 |        NESTED LOOPS                  |                             |       |       |         |     |
|  33 |         NESTED LOOPS                 |                             |     1 |    55 |     5  (20)| 00:00:01 |
|  34 |          VIEW                        | VW_NSO_1                    |     5 |    30 |     2   (0)| 00:00:01 |
|  35 |           HASH UNIQUE                |                             |     1 |    30 |         |     |
|  36 |            VIEW                      |                             |     5 |    30 |     2   (0)| 00:00:01 |
|  37 |             TABLE ACCESS FULL        | SYS_TEMP_0FD9D6FDF_24D7DA10 |     5 |    30 |     2   (0)| 00:00:01 |
|* 38 |          INDEX UNIQUE SCAN           | PK_LO_ENTITY                |     1 |       |     1   (0)| 00:00:01 |
|  39 |         TABLE ACCESS BY INDEX ROWID  | LO_ENTITY                   |     1 |    49 |     2   (0)| 00:00:01 |
|* 40 |        TABLE ACCESS FULL             | LO_ENTITY_TEMP              |     2 |    84 |   581   (1)| 00:00:07 |
|  41 |       VIEW                           | VW_NSO_2                    |    15 |   120 |    14  (15)| 00:00:01 |
|* 42 |        HASH JOIN                     |                             |    15 |   660 |    14  (15)| 00:00:01 |
|  43 |         VIEW                         | V_TEMP_ANZ_CMF              |     3 |   114 |    11  (10)| 00:00:01 |
|  44 |          UNION-ALL                   |                             |       |       |         |     |
|* 45 |           TABLE ACCESS FULL          | ANZ_CMF_TEMP                |     1 |    25 |     3   (0)| 00:00:01 |
|* 46 |           FILTER                     |                             |       |       |         |     |
|  47 |            MERGE JOIN CARTESIAN      |                             |     1 |    17 |     5  (20)| 00:00:01 |
|  48 |             VIEW                     |                             |     1 |     9 |     2  (50)| 00:00:01 |
|  49 |              HASH UNIQUE             |                             |     1 |     9 |     2  (50)| 00:00:01 |
|* 50 |               INDEX RANGE SCAN       | I1_ANZ_CMF_TEMP             |     1 |     9 |     1   (0)| 00:00:01 |
|  51 |             BUFFER SORT              |                             |     1 |     8 |     5  (20)| 00:00:01 |
|  52 |              TABLE ACCESS FULL       | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |
|* 53 |            TABLE ACCESS FULL         | ANZ_CMF_TEMP                |     1 |    17 |     3   (0)| 00:00:01 |
|* 54 |           FILTER                     |                             |       |       |         |     |
|  55 |            TABLE ACCESS FULL         | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |
|  56 |         VIEW                         |                             |     5 |    30 |     2   (0)| 00:00:01 |
|  57 |          TABLE ACCESS FULL           | SYS_TEMP_0FD9D6FDF_24D7DA10 |     5 |    30 |     2   (0)| 00:00:01 |
|  58 |    VIEW                              |                             |     2 |   258 |  1189   (1)| 00:00:15 |
|  59 |     SORT ORDER BY                    |                             |     2 |   376 |  1189   (1)| 00:00:15 |
|  60 |      VIEW                            |                             |     2 |   376 |  1188   (1)| 00:00:15 |
|  61 |       HASH UNIQUE                    |                             |     2 |   288 |  1188   (1)| 00:00:15 |
|* 62 |        HASH JOIN                     |                             |    15 |  2160 |  1187   (1)| 00:00:15 |
|  63 |         NESTED LOOPS OUTER           |                             |     3 |   414 |  1184   (1)| 00:00:15 |
|  64 |          NESTED LOOPS OUTER          |                             |     3 |   390 |  1181   (1)| 00:00:15 |
|* 65 |           HASH JOIN OUTER            |                             |     3 |   291 |  1175   (1)| 00:00:15 |
|* 66 |            HASH JOIN OUTER           |                             |     3 |   231 |   593   (1)| 00:00:08 |
|  67 |             VIEW                     | V_TEMP_ANZ_CMF              |     3 |   150 |    11  (10)| 00:00:01 |
|  68 |              UNION-ALL               |                             |       |       |         |     |
|* 69 |               TABLE ACCESS FULL      | ANZ_CMF_TEMP                |     1 |    37 |     3   (0)| 00:00:01 |
|* 70 |               FILTER                 |                             |       |       |         |     |
|  71 |                MERGE JOIN CARTESIAN  |                             |     1 |    29 |     5  (20)| 00:00:01 |
|  72 |                 VIEW                 |                             |     1 |     9 |     2  (50)| 00:00:01 |
|  73 |                  HASH UNIQUE         |                             |     1 |     9 |     2  (50)| 00:00:01 |
|* 74 |                   INDEX RANGE SCAN   | I1_ANZ_CMF_TEMP             |     1 |     9 |     1   (0)| 00:00:01 |
|  75 |                 BUFFER SORT          |                             |     1 |    20 |     5  (20)| 00:00:01 |
|  76 |                  TABLE ACCESS FULL   | ANZ_CMF                     |     1 |    20 |     3   (0)| 00:00:01 |
|* 77 |                TABLE ACCESS FULL     | ANZ_CMF_TEMP                |     1 |    17 |     3   (0)| 00:00:01 |
|* 78 |               FILTER                 |                             |       |       |         |     |
|  79 |                TABLE ACCESS FULL     | ANZ_CMF                     |     1 |    20 |     3   (0)| 00:00:01 |
|* 80 |             TABLE ACCESS FULL        | LO_ENTITY_TEMP              |     2 |    54 |   581   (1)| 00:00:07 |
|* 81 |            TABLE ACCESS FULL         | LO_ENTITY_TEMP              |     2 |    40 |   581   (1)| 00:00:07 |
|  82 |           TABLE ACCESS BY INDEX ROWID| LO_ENTITY                   |     1 |    33 |     2   (0)| 00:00:01 |
|* 83 |            INDEX UNIQUE SCAN         | I6_LO_ENTITY                |     1 |       |     1   (0)| 00:00:01 |
|* 84 |          INDEX UNIQUE SCAN           | I6_LO_ENTITY                |     1 |     8 |     1   (0)| 00:00:01 |
|  85 |         VIEW                         |                             |     5 |    30 |     2   (0)| 00:00:01 |
|  86 |          TABLE ACCESS FULL           | SYS_TEMP_0FD9D6FDF_24D7DA10 |     5 |    30 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------------

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

   3 - access("MOTHER_CODE"=PRIOR "CHILD_CODE")
   6 - filter("MOTHER_CODE"=568 AND "TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy' AND
              "TEMP_STATUS"<>'DELETED')
   7 - access("PROCESS_ID"='11838873')
   9 - filter("TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy')
  10 - access("MOTHER_CODE"=568)
  11 - filter("L"."ID"="T"."ID")
  12 - access("T"."PROCESS_ID"='11838873')
  15 - access("connect$_by$_pump$_005"."PRIOR child_code "="MOTHER_CODE")
  16 - filter("TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy' AND "TEMP_STATUS"<>'DELETED')
  17 - access("PROCESS_ID"='11838873')
  19 - access("L"."ID"="T"."ID")
  23 - access("connect$_by$_pump$_005"."PRIOR child_code "="MOTHER_CODE")
  24 - filter("TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy')
  26 - access("T"."PROCESS_ID"='11838873')
  30 - access("EN"."ENTITY_CODE"="ENTITY_CODE")
  31 - access("EN"."ENTITY_CODE"="TA"."ENTITY_CODE"(+))
  38 - access("EN"."ID"="CHILD_CODE")
  40 - filter("TA"."PROCESS_ID"(+)='11838873' AND "TA"."TEMP_STATUS"(+)<>'ORIGIN' AND
              "TA"."TEMP_STATUS"(+)<>'DELETED' AND "TA"."PROCESS_TYPE"(+)='WORKFLOW')
  42 - access("SUB_ECO"."ENTITY_CODE"=TO_CHAR("CHILD_CODE"))
  45 - filter("PROCESS_ID"='11838873' AND "TEMP_STATUS"<>'DELETED')
  46 - filter( NOT EXISTS (SELECT 0 FROM RO_ANZ_CI."ANZ_CMF_TEMP" "ANZ_CMF_TEMP" WHERE "PROCESS_ID"=:B1 AND
              "ENTITY_CODE"=:B2))
  50 - access("PROCESS_ID"='11838873')
  53 - filter("PROCESS_ID"=:B1 AND "ENTITY_CODE"=:B2)
  54 - filter(NULL IS NOT NULL AND NULL IS NOT NULL)
  62 - access("CMF"."ENTITY_CODE"=TO_CHAR("CHILD_CODE"))
  65 - access("JTEMP"."ENTITY_CODE"(+)="CMF"."GRP")
  66 - access("CMF"."ENTITY_CODE"="TENT"."ENTITY_CODE"(+))
  69 - filter("PROCESS_ID"='11838873' AND "TEMP_STATUS"<>'DELETED')
  70 - filter( NOT EXISTS (SELECT 0 FROM RO_ANZ_CI."ANZ_CMF_TEMP" "ANZ_CMF_TEMP" WHERE "PROCESS_ID"=:B1 AND
              "ENTITY_CODE"=:B2))
  74 - access("PROCESS_ID"='11838873')
  77 - filter("PROCESS_ID"=:B1 AND "ENTITY_CODE"=:B2)
  78 - filter(NULL IS NOT NULL AND NULL IS NOT NULL)
  80 - filter("TENT"."PROCESS_ID"(+)='11838873' AND "TENT"."TEMP_STATUS"(+)<>'ORIGIN' AND
              "TENT"."TEMP_STATUS"(+)<>'DELETED')
  81 - filter("JTEMP"."PROCESS_ID"(+)='11838873' AND "JTEMP"."TEMP_STATUS"(+)<>'ORIGIN' AND
              "JTEMP"."TEMP_STATUS"(+)<>'DELETED')
  83 - access("CMF"."ENTITY_CODE"="ENT"."ENTITY_CODE"(+))
  84 - access("JENT"."ENTITY_CODE"(+)="CMF"."GRP")


Statistics
----------------------------------------------------------
         59  recursive calls
      22053  db block gets
    5642276  consistent gets
          1  physical reads
        676  redo size
        667  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          8  sorts (memory)
          0  sorts (disk)
          2  rows processed

SQL>

 

先不看至今计划,单看执行时间 - 大约40秒,逻辑读 5,642,276

 

如果把UNION ALL 改成 UNION 的话,执行时间下降为2秒,逻辑读下降为106,830 !

 

Elapsed: 00:00:02.04

Execution Plan
----------------------------------------------------------
Plan hash value: 1306395122

----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                              | Name                        | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                       |                             |    20 |  3350 |  1863  (66)| 00:00:23 |
|   1 |  TEMP TABLE TRANSFORMATION             |                             |       |       |         |             |
|   2 |   LOAD AS SELECT                       | SYS_TEMP_0FD9D6FE3_24D7DA10 |       |       |         |             |
|*  3 |    CONNECT BY WITH FILTERING           |                             |       |       |         |             |
|   4 |     VIEW                               |                             |     2 |    52 |     8   (0)| 00:00:01 |
|   5 |      UNION-ALL                         |                             |       |       |         |             |
|*  6 |       TABLE ACCESS BY INDEX ROWID      | LO_ENTITY_LINKS_TEMP        |     1 |    52 |     3   (0)| 00:00:01 |
|*  7 |        INDEX RANGE SCAN                | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|   8 |       NESTED LOOPS ANTI                |                             |     1 |    57 |     5   (0)| 00:00:01 |
|*  9 |        TABLE ACCESS BY INDEX ROWID     | LO_ENTITY_LINKS             |     1 |    45 |     2   (0)| 00:00:01 |
|* 10 |         INDEX RANGE SCAN               | I1_LO_ENTITY_LINKS          |     1 |       |     1   (0)| 00:00:01 |
|* 11 |        TABLE ACCESS BY INDEX ROWID     | LO_ENTITY_LINKS_TEMP        |     1 |    12 |     3   (0)| 00:00:01 |
|* 12 |         INDEX RANGE SCAN               | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|  13 |     VIEW                               |                             |     3 |    78 |    27   (4)| 00:00:01 |
|  14 |      UNION-ALL                         |                             |       |       |         |             |
|* 15 |       HASH JOIN                        |                             |     1 |    65 |    12   (9)| 00:00:01 |
|* 16 |        TABLE ACCESS BY INDEX ROWID     | LO_ENTITY_LINKS_TEMP        |     1 |    52 |     3   (0)| 00:00:01 |
|* 17 |         INDEX RANGE SCAN               | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|  18 |        CONNECT BY PUMP                 |                             |       |       |         |             |
|* 19 |       HASH JOIN ANTI                   |                             |     2 |   140 |    16   (7)| 00:00:01 |
|  20 |        NESTED LOOPS                    |                             |       |       |         |             |
|  21 |         NESTED LOOPS                   |                             |     2 |   116 |    12   (0)| 00:00:01 |
|  22 |          CONNECT BY PUMP               |                             |       |       |         |             |
|* 23 |          INDEX RANGE SCAN              | I1_LO_ENTITY_LINKS          |     1 |       |     1   (0)| 00:00:01 |
|* 24 |         TABLE ACCESS BY INDEX ROWID    | LO_ENTITY_LINKS             |     1 |    45 |     2   (0)| 00:00:01 |
|  25 |        TABLE ACCESS BY INDEX ROWID     | LO_ENTITY_LINKS_TEMP        |     1 |    12 |     3   (0)| 00:00:01 |
|* 26 |         INDEX RANGE SCAN               | LINKS_TEMP                  |     1 |       |     1   (0)| 00:00:01 |
|  27 |   SORT UNIQUE                          |                             |    20 |  3350 |  1863  (66)| 00:00:23 |
|  28 |    UNION-ALL                           |                             |       |       |         |             |
|* 29 |     HASH JOIN ANTI                     |                             |     5 |   575 |   641   (1)| 00:00:08 |
|* 30 |      HASH JOIN OUTER                   |                             |     5 |   440 |   594   (1)| 00:00:08 |
|  31 |       NESTED LOOPS                     |                             |       |       |         |             |
|  32 |        NESTED LOOPS                    |                             |     5 |   260 |    12   (0)| 00:00:01 |
|  33 |         VIEW                           |                             |     5 |    65 |     2   (0)| 00:00:01 |
|  34 |          TABLE ACCESS FULL             | SYS_TEMP_0FD9D6FE3_24D7DA10 |     5 |    30 |     2   (0)| 00:00:01 |
|* 35 |         INDEX UNIQUE SCAN              | PK_LO_ENTITY                |     1 |       |     1   (0)| 00:00:01 |
|  36 |        TABLE ACCESS BY INDEX ROWID     | LO_ENTITY                   |     1 |    39 |     2   (0)| 00:00:01 |
|* 37 |       TABLE ACCESS FULL                | LO_ENTITY_TEMP              |     2 |    72 |   581   (1)| 00:00:07 |
|  38 |      VIEW                              | VW_NSO_1                    |    15 |   405 |    47   (5)| 00:00:01 |
|* 39 |       HASH JOIN                        |                             |    15 |  1050 |    47   (5)| 00:00:01 |
|  40 |        VIEW                            | V_TEMP_ANZ_CMF              |     3 |   171 |    44   (3)| 00:00:01 |
|  41 |         UNION-ALL                      |                             |       |       |         |             |
|* 42 |          TABLE ACCESS FULL             | ANZ_CMF_TEMP                |     1 |    25 |     3   (0)| 00:00:01 |
|* 43 |          FILTER                        |                             |       |       |         |             |
|  44 |           MERGE JOIN CARTESIAN         |                             |     1 |    35 |     5  (20)| 00:00:01 |
|  45 |            VIEW                        |                             |     1 |    27 |     2  (50)| 00:00:01 |
|  46 |             HASH UNIQUE                |                             |     1 |     9 |     2  (50)| 00:00:01 |
|* 47 |              INDEX RANGE SCAN          | I1_ANZ_CMF_TEMP             |     1 |     9 |     1   (0)| 00:00:01 |
|  48 |            BUFFER SORT                 |                             |     1 |     8 |     5  (20)| 00:00:01 |
|  49 |             TABLE ACCESS FULL          | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |
|* 50 |           TABLE ACCESS BY INDEX ROWID  | ANZ_CMF_TEMP                |     1 |    17 |    36   (0)| 00:00:01 |
|* 51 |            INDEX RANGE SCAN            | I1_ANZ_CMF_TEMP             |    41 |       |     1   (0)| 00:00:01 |
|* 52 |          FILTER                        |                             |       |       |         |             |
|  53 |           TABLE ACCESS FULL            | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |
|  54 |        VIEW                            |                             |     5 |    65 |     2   (0)| 00:00:01 |
|  55 |         TABLE ACCESS FULL              | SYS_TEMP_0FD9D6FE3_24D7DA10 |     5 |    30 |     2   (0)| 00:00:01 |
|* 56 |     HASH JOIN                          |                             |    15 |  2775 |  1220   (1)| 00:00:15 |
|  57 |      NESTED LOOPS OUTER                |                             |     3 |   516 |  1217   (1)| 00:00:15 |
|  58 |       NESTED LOOPS OUTER               |                             |     3 |   492 |  1214   (1)| 00:00:15 |
|* 59 |        HASH JOIN OUTER                 |                             |     3 |   393 |  1208   (1)| 00:00:15 |
|* 60 |         HASH JOIN OUTER                |                             |     3 |   333 |   626   (1)| 00:00:08 |
|  61 |          VIEW                          | V_TEMP_ANZ_CMF              |     3 |   252 |    44   (3)| 00:00:01 |
|  62 |           UNION-ALL                    |                             |       |       |         |             |
|* 63 |            TABLE ACCESS FULL           | ANZ_CMF_TEMP                |     1 |    37 |     3   (0)| 00:00:01 |
|* 64 |            FILTER                      |                             |       |       |         |             |
|  65 |             MERGE JOIN CARTESIAN       |                             |     1 |    47 |     5  (20)| 00:00:01 |
|  66 |              VIEW                      |                             |     1 |    27 |     2  (50)| 00:00:01 |
|  67 |               HASH UNIQUE              |                             |     1 |     9 |     2  (50)| 00:00:01 |
|* 68 |                INDEX RANGE SCAN        | I1_ANZ_CMF_TEMP             |     1 |     9 |     1   (0)| 00:00:01 |
|  69 |              BUFFER SORT               |                             |     1 |    20 |     5  (20)| 00:00:01 |
|  70 |               TABLE ACCESS FULL        | ANZ_CMF                     |     1 |    20 |     3   (0)| 00:00:01 |
|* 71 |             TABLE ACCESS BY INDEX ROWID| ANZ_CMF_TEMP                |     1 |    17 |    36   (0)| 00:00:01 |
|* 72 |              INDEX RANGE SCAN          | I1_ANZ_CMF_TEMP             |    41 |       |     1   (0)| 00:00:01 |
|* 73 |            FILTER                      |                             |       |       |         |             |
|  74 |             TABLE ACCESS FULL          | ANZ_CMF                     |     1 |    20 |     3   (0)| 00:00:01 |
|* 75 |          TABLE ACCESS FULL             | LO_ENTITY_TEMP              |     2 |    54 |   581   (1)| 00:00:07 |
|* 76 |         TABLE ACCESS FULL              | LO_ENTITY_TEMP              |     2 |    40 |   581   (1)| 00:00:07 |
|  77 |        TABLE ACCESS BY INDEX ROWID     | LO_ENTITY                   |     1 |    33 |     2   (0)| 00:00:01 |
|* 78 |         INDEX UNIQUE SCAN              | I6_LO_ENTITY                |     1 |       |     1   (0)| 00:00:01 |
|* 79 |       INDEX UNIQUE SCAN                | I6_LO_ENTITY                |     1 |     8 |     1   (0)| 00:00:01 |
|  80 |      VIEW                              |                             |     5 |    65 |     2   (0)| 00:00:01 |
|  81 |       TABLE ACCESS FULL                | SYS_TEMP_0FD9D6FE3_24D7DA10 |     5 |    30 |     2   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------------------

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

   3 - access("MOTHER_CODE"=PRIOR "CHILD_CODE")
   6 - filter("MOTHER_CODE"=568 AND "TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy' AND
              "TEMP_STATUS"<>'DELETED')
   7 - access("PROCESS_ID"='11838873')
   9 - filter("TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy')
  10 - access("MOTHER_CODE"=568)
  11 - filter("L"."ID"="T"."ID")
  12 - access("T"."PROCESS_ID"='11838873')
  15 - access("connect$_by$_pump$_005"."PRIOR child_code "="MOTHER_CODE")
  16 - filter("TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy' AND "TEMP_STATUS"<>'DELETED')
  17 - access("PROCESS_ID"='11838873')
  19 - access("L"."ID"="T"."ID")
  23 - access("connect$_by$_pump$_005"."PRIOR child_code "="MOTHER_CODE")
  24 - filter("TYPE"='Customer Grouping' AND "PURPOSE"='Credit Hierarchy')
  26 - access("T"."PROCESS_ID"='11838873')
  29 - access("EN"."ENTITY_CODE"="ENTITY_CODE")
  30 - access("EN"."ENTITY_CODE"="TA"."ENTITY_CODE"(+))
  35 - access("EN"."ID"="CHILD_CODE")
  37 - filter("TA"."PROCESS_ID"(+)='11838873' AND "TA"."TEMP_STATUS"(+)<>'ORIGIN' AND
              "TA"."TEMP_STATUS"(+)<>'DELETED' AND "TA"."PROCESS_TYPE"(+)='WORKFLOW')
  39 - access("SUB_ECO"."ENTITY_CODE"=TO_CHAR("CHILD_CODE"))
  42 - filter("PROCESS_ID"='11838873' AND "TEMP_STATUS"<>'DELETED')
  43 - filter( NOT EXISTS (SELECT 0 FROM RO_ANZ_CI."ANZ_CMF_TEMP" "ANZ_CMF_TEMP" WHERE "PROCESS_ID"=:B1 AND
              "ENTITY_CODE"=:B2))
  47 - access("PROCESS_ID"='11838873')
  50 - filter("ENTITY_CODE"=:B1)
  51 - access("PROCESS_ID"=:B1)
  52 - filter(NULL IS NOT NULL AND NULL IS NOT NULL)
  56 - access("CMF"."ENTITY_CODE"=TO_CHAR("CHILD_CODE"))
  59 - access("JTEMP"."ENTITY_CODE"(+)="CMF"."GRP")
  60 - access("CMF"."ENTITY_CODE"="TENT"."ENTITY_CODE"(+))
  63 - filter("PROCESS_ID"='11838873' AND "TEMP_STATUS"<>'DELETED')
  64 - filter( NOT EXISTS (SELECT 0 FROM RO_ANZ_CI."ANZ_CMF_TEMP" "ANZ_CMF_TEMP" WHERE "PROCESS_ID"=:B1 AND
              "ENTITY_CODE"=:B2))
  68 - access("PROCESS_ID"='11838873')
  71 - filter("ENTITY_CODE"=:B1)
  72 - access("PROCESS_ID"=:B1)
  73 - filter(NULL IS NOT NULL AND NULL IS NOT NULL)
  75 - filter("TENT"."PROCESS_ID"(+)='11838873' AND "TENT"."TEMP_STATUS"(+)<>'ORIGIN' AND
              "TENT"."TEMP_STATUS"(+)<>'DELETED')
  76 - filter("JTEMP"."PROCESS_ID"(+)='11838873' AND "JTEMP"."TEMP_STATUS"(+)<>'ORIGIN' AND
              "JTEMP"."TEMP_STATUS"(+)<>'DELETED')
  78 - access("CMF"."ENTITY_CODE"="ENT"."ENTITY_CODE"(+))
  79 - access("JENT"."ENTITY_CODE"(+)="CMF"."GRP")


Statistics
----------------------------------------------------------
         56  recursive calls
         13  db block gets
     106830  consistent gets
          1  physical reads
       1344  redo size
        667  bytes sent via SQL*Net to client
        499  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          7  sorts (memory)
          0  sorts (disk)
          2  rows processed

SQL>

 

很显然,造成执行时间差别这么大的原因在于逻辑读的差别。逻辑读的差别肯定来自于执行计划的差别。比较下两种情况下的执行计划,主要不同在于访问表ANZ_CMF_TEMP的方式不同 - 用UNION ALL的时候没有用索引扫描!

(1) UNION ALL

|  43 |         VIEW                         | V_TEMP_ANZ_CMF              |     3 |   114 |    11  (10)| 00:00:01 |
|  44 |          UNION-ALL                   |                             |       |       |         |     |
|* 45 |           TABLE ACCESS FULL          | ANZ_CMF_TEMP                |     1 |    25 |     3   (0)| 00:00:01 |
|* 46 |           FILTER                     |                             |       |       |         |     |
|  47 |            MERGE JOIN CARTESIAN      |                             |     1 |    17 |     5  (20)| 00:00:01 |
|  48 |             VIEW                     |                             |     1 |     9 |     2  (50)| 00:00:01 |
|  49 |              HASH UNIQUE             |                             |     1 |     9 |     2  (50)| 00:00:01 |
|* 50 |               INDEX RANGE SCAN       | I1_ANZ_CMF_TEMP             |     1 |     9 |     1   (0)| 00:00:01 |
|  51 |             BUFFER SORT              |                             |     1 |     8 |     5  (20)| 00:00:01 |
|  52 |              TABLE ACCESS FULL       | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |
|* 53 |            TABLE ACCESS FULL         | ANZ_CMF_TEMP                |     1 |    17 |     3   (0)| 00:00:01 |
|* 54 |           FILTER                     |                             |       |       |         |     |
|  55 |            TABLE ACCESS FULL         | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |

 

(2) UNION

|  40 |        VIEW                            | V_TEMP_ANZ_CMF              |     3 |   171 |    44   (3)| 00:00:01 |
|  41 |         UNION-ALL                      |                             |       |       |         |             |
|* 42 |          TABLE ACCESS FULL             | ANZ_CMF_TEMP                |     1 |    25 |     3   (0)| 00:00:01 |
|* 43 |          FILTER                        |                             |       |       |         |             |
|  44 |           MERGE JOIN CARTESIAN         |                             |     1 |    35 |     5  (20)| 00:00:01 |
|  45 |            VIEW                        |                             |     1 |    27 |     2  (50)| 00:00:01 |
|  46 |             HASH UNIQUE                |                             |     1 |     9 |     2  (50)| 00:00:01 |
|* 47 |              INDEX RANGE SCAN          | I1_ANZ_CMF_TEMP             |     1 |     9 |     1   (0)| 00:00:01 |
|  48 |            BUFFER SORT                 |                             |     1 |     8 |     5  (20)| 00:00:01 |
|  49 |             TABLE ACCESS FULL          | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |
|* 50 |           TABLE ACCESS BY INDEX ROWID  | ANZ_CMF_TEMP                |     1 |    17 |    36   (0)| 00:00:01 |
|* 51 |            INDEX RANGE SCAN            | I1_ANZ_CMF_TEMP             |    41 |       |     1   (0)| 00:00:01 |
|* 52 |          FILTER                        |                             |       |       |         |             |
|  53 |           TABLE ACCESS FULL            | ANZ_CMF                     |     1 |     8 |     3   (0)| 00:00:01 |

 

这个例子告诉我们用UNION ALL去替换UNION的时候要谨防执行计划的改变!

 

其实这个SQL如果只是执行UNION (ALL)的上面的SQL,或是下面的SQL都会很慢,因为执行计划都是走了表扫描的方式来访问ANZ_CMP_TEMP。如果要改变执行计划(比如只执行UNION的上面部分)怎么办呢,基于这个例子启发,尝试用了如下的写法(highlight部分),结果执行计划就走了索引扫描。

WITH v_tmp_entity_code AS
(
    SELECT CHILD_CODE
    FROM
    ( 
        SELECT     child_code,
                mother_code
        FROM     lo_entity_links_temp
        WHERE     process_id = '11838873'
        AND     temp_status <> 'DELETED'
        AND     purpose      = 'Credit Hierarchy'
        AND     TYPE         = 'Customer Grouping'
      UNION ALL
        SELECT     child_code,
                mother_code
        FROM     lo_entity_links l
        WHERE     purpose = 'Credit Hierarchy'
        AND     TYPE    = 'Customer Grouping'
        AND NOT EXISTS
        (
            SELECT     NULL
            FROM     lo_entity_links_temp t
            WHERE     l.id       = t.id
            AND     t.process_id = '11838873'
        )
    )
    START WITH MOTHER_CODE      = 568
    CONNECT BY PRIOR child_code = MOTHER_CODE
)
SELECT     UP_SUB.ENTITY_CODE AS "ENTITY_CODE",
        UP_SUB.ENTITY_NAME      AS "ENTITY_NAME"
FROM
  (
  SELECT      EN.ENTITY_CODE,
            DECODE(TA.ATTRIBUTE_6, NULL, EN.ATTRIBUTE_6, TA.ATTRIBUTE_6)                                              AS "ATTRIBUTE_6",
            DECODE(TA.incorporation_country_code, NULL, EN.incorporation_country_code, TA.incorporation_country_code) AS "INCORP_COUNTRY",
            DECODE(TA.LONG_NAME, NULL, EN.LONG_NAME, TA.LONG_NAME)                                                    AS "ENTITY_NAME",
            DECODE( TA.IS_BLACKBOOK, 'T', 1, 0) IS_BLACKBOOK_CP
  FROM         LO_ENTITY EN
  LEFT OUTER JOIN CD_USERS U  ON EN.ATTRIBUTE_7 = U.USER_NAME
  LEFT OUTER JOIN LO_ENTITY_TEMP TA ON EN.ENTITY_CODE   = TA.ENTITY_CODE
                                      AND TA.process_id   = '11838873'
                                      AND TA.TEMP_STATUS <> 'ORIGIN'
                                      AND TA.TEMP_STATUS <> 'DELETED'
                                      AND TA.process_type = 'WORKFLOW'
  WHERE  EN.id IN
    (
        SELECT child_code FROM v_tmp_entity_code
    )
  AND EN.entity_code NOT IN
    (
        SELECT     SUB_ECO.ENTITY_CODE
        FROM     V_TEMP_ANZ_CMF SUB_ECO
        WHERE     SUB_ECO.is_temp    = 'Y'
        AND     SUB_ECO.process_id   = '11838873'
        AND     SUB_ECO.ENTITY_CODE IN
        (
            SELECT TO_CHAR(child_code) FROM v_tmp_entity_code
      )
    )
  ORDER BY EN.LONG_NAME
  ) UP_SUB
  UNION 
  SELECT NULL, NULL FROM DUAL WHERE 1=0;

 

你可能感兴趣的:(Is "UNION ALL" Always Better Than "UNION"? Watch Out!)