• IN , EXIST


    在SQL优化中,经常会有人问IN,NOT IN,NOT EXIST, EXIST的区别,会问IN 和 EXIST, NOT IN 和 NOT EXIST做比较哪些会更快些。  以前在RBO时代也许他们还是有区别的,但是现在oracle都是用CBO来处理SQL了,应该不会有区别 但这只是猜想,还是要试试看才行。

    首先,作比较的话 应该是这样比较 IN 和 EXIST一对儿作比较, NOT IN , NOT EXIST 比较 ,我们先看看IN , EXIST。 其他的文章再讨论 NOT IN 和 NOT  EXIST

    IN && EXIST

    用in 和 exist的情况一般都是下面这样。

    SELECT * FROM t1 WHERE x IN ( SELECT y FROM t2);

    SELECT * FROM t1 WHERE EXISTS ( SELECT NULL FROM t2 WHERE y=t1.x);

    根据ASK TOM 上的回答 (http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:953229842074)这两者的区别如下。

    SELECT * FROM t1 WHERE x IN ( SELECT y FROM t2);

    的执行模式应该是:

    select * 
    from t1, ( select distinct y from t2 ) t2
    where t1.x = t2.y;

    The subquery is evaluated, distinct'ed, indexed (or hashed or sorted) and then joined to 
    the original table -- typically.

    SELECT * FROM t1 WHERE EXISTS ( SELECT NULL FROM t2 WHERE y=t1.x);

    的执行模式应该是:

    for x in ( select * from t1 )
       loop
          if ( exists ( select null from t2 where y = x.x )
          then 
             OUTPUT THE RECORD
          end if
       end loop

    It always results in a full scan of T1 whereas the first query can make use of an index 
    on T1(x).

    那么是不是这样的呢? 因为TOM 用的是8i,他当时的环境可能是RBO 也可能是CBO。 我们都测试一下。

    首先测试RBO模式

    1. 设置使用RBO来优化SQL

    SQL> alter session set optimizer_mode='RULE';
    
    Session altered.

    2. 执行语句查看指向计划 IN的执行计划

    SQL> select /*+ gather_plan_statistics */ * from emp where deptno in ( select DEPTNO from dept where DNAME='ACCOUNTING');
    
         EMPNO ENAME    JOB               MGR HIREDATE                   SAL       COMM     DEPTNO
    ---------- -------- ---------- ---------- ------------------- ---------- ---------- ----------
          7782 CLARK    MANAGER          7839 1981-06-09 00:00:00       2450                    10
          7839 KING     PRESIDENT             1981-11-17 00:00:00       5000                    10
          7934 MILLER   CLERK            7782 1982-01-23 00:00:00       9999                    10
    
    SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
    
    -----------------------------------------------------------------------------------
    | Id  | Operation             | Name     | Starts | A-Rows |   A-Time   | Buffers |
    -----------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT      |          |      1 |      3 |00:00:00.01 |       6 |
    |   1 |  MERGE JOIN           |          |      1 |      3 |00:00:00.01 |       6 |
    |   2 |   SORT JOIN           |          |      1 |      4 |00:00:00.01 |       3 |
    |   3 |    TABLE ACCESS FULL  | EMP      |      1 |     14 |00:00:00.01 |       3 |
    |*  4 |   SORT JOIN           |          |      4 |      3 |00:00:00.01 |       3 |
    |   5 |    VIEW               | VW_NSO_1 |      1 |      1 |00:00:00.01 |       3 |
    |   6 |     SORT UNIQUE       |          |      1 |      1 |00:00:00.01 |       3 |
    |*  7 |      TABLE ACCESS FULL| DEPT     |      1 |      1 |00:00:00.01 |       3 |
    -----------------------------------------------------------------------------------
    
       4 - access("DEPTNO"="$nso_col_1")
           filter("DEPTNO"="$nso_col_1")
       7 - filter("DNAME"='ACCOUNTING')
    
    Note
    -----
       - rule based optimizer used (consider using cbo)

    完全符合TOM的说法(如下)

    select * 
    from t1, ( select distinct y from t2 ) t2
    where t1.x = t2.y;


    The subquery is evaluated, distinct'ed, indexed (or hashed or sorted) and then joined to the original table -- typically.

    IN后面的子查询被看成一个view,也就是上面执行计划的5,6,7步骤。 然后这个VIEW和原来的表做连接。

    3. 再看一下EXIST的执行计划 

    SQL> select /*+ gather_plan_statistics */ * from emp where exists (select 8 from dept where dname='ACCOUNTING' and deptno=emp.deptno);
    
         EMPNO ENAME    JOB               MGR HIREDATE                   SAL       COMM     DEPTNO
    ---------- -------- ---------- ---------- ------------------- ---------- ---------- ----------
          7782 CLARK    MANAGER          7839 1981-06-09 00:00:00       2450                    10
          7839 KING     PRESIDENT             1981-11-17 00:00:00       5000                    10
          7934 MILLER   CLERK            7782 1982-01-23 00:00:00       9999                    10
    
    SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
    
    ----------------------------------------------------------------------------
    | Id  | Operation          | Name | Starts | A-Rows |   A-Time   | Buffers |
    ----------------------------------------------------------------------------
    |   0 | SELECT STATEMENT   |      |      1 |      3 |00:00:00.01 |      13 |
    |*  1 |  FILTER            |      |      1 |      3 |00:00:00.01 |      13 |
    |   2 |   TABLE ACCESS FULL| EMP  |      1 |     14 |00:00:00.01 |       4 |
    |*  3 |   TABLE ACCESS FULL| DEPT |      3 |      1 |00:00:00.01 |       9 |
    ----------------------------------------------------------------------------
    
       1 - filter( IS NOT NULL)
       3 - filter(("DEPTNO"=:B1 AND "DNAME"='ACCOUNTING'))
    
    Note
    -----
       - rule based optimizer used (consider using cbo)
    
    
    26 rows selected.

    这里面至关重要的是理解FILTER这个操作。 在ORACLE性能优化艺术一书中,它被归结为相关联合性操作,如果不理解这是什么意思也没关系,你只需要知道FILTER在这里的意思是,让执行计划的第二行来驱动第三行,也就是第二行返回的结果,每一行都会驱动第三行跑一次。 这正是tom所说的情况(如下)。

    for x in ( select * from t1 )
       loop
          if ( exists ( select null from t2 where y = x.x )
          then 
             OUTPUT THE RECORD
          end if
       end loop
    
    It always results in a full scan of T1 whereas the first query can make use of an index 
    on T1(x).

     但是,不要忘了,上面的还是在RBO下做的,RBO现在已经是历史了,所以我们要在CBO下再测试一次。

    下面这一段是in 模式

    SQL> alter session set optimizer_mode='ALL_ROWS';
    
    Session altered.
    
    SQL> select /*+ gather_plan_statistics */ * from emp where deptno in ( select DEPTNO from dept where DNAME='ACCOUNTING');
    
         EMPNO ENAME    JOB               MGR HIREDATE                   SAL       COMM     DEPTNO
    ---------- -------- ---------- ---------- ------------------- ---------- ---------- ----------
          7934 MILLER   CLERK            7782 1982-01-23 00:00:00       9999                    10
          7839 KING     PRESIDENT             1981-11-17 00:00:00       5000                    10
          7782 CLARK    MANAGER          7839 1981-06-09 00:00:00       2450                    10
    
    SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
    
    ------------------------------------------------------------------------------------------
    | Id  | Operation           | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
    ------------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT    |          |      1 |        |      3 |00:00:00.01 |       7 |
    |*  1 |  HASH JOIN SEMI     |          |      1 |      5 |      3 |00:00:00.01 |       7 |
    |   2 |   TABLE ACCESS FULL | EMP      |      1 |     14 |     14 |00:00:00.01 |       3 |
    |   3 |   VIEW              | VW_NSO_1 |      1 |      1 |      1 |00:00:00.01 |       4 |
    |*  4 |    TABLE ACCESS FULL| DEPT     |      1 |      1 |      1 |00:00:00.01 |       4 |
    ------------------------------------------------------------------------------------------
    
       1 - access("DEPTNO"="$nso_col_1")
       4 - filter("DNAME"='ACCOUNTING')
    
    
    23 rows selected.

     下面这一段是 exist模式

    SQL> select /*+ gather_plan_statistics */ * from emp where exists (select 8 from dept where dname='ACCOUNTING' and deptno=emp.deptno);
    
         EMPNO ENAME    JOB               MGR HIREDATE                   SAL       COMM     DEPTNO
    ---------- -------- ---------- ---------- ------------------- ---------- ---------- ----------
          7934 MILLER   CLERK            7782 1982-01-23 00:00:00       9999                    10
          7839 KING     PRESIDENT             1981-11-17 00:00:00       5000                    10
          7782 CLARK    MANAGER          7839 1981-06-09 00:00:00       2450                    10
    
    SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
    
    -----------------------------------------------------------------------------------------
    | Id  | Operation           | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
    -----------------------------------------------------------------------------------------
    |   0 | SELECT STATEMENT    |         |      1 |        |      3 |00:00:00.01 |       7 |
    |*  1 |  HASH JOIN SEMI     |         |      1 |      5 |      3 |00:00:00.01 |       7 |
    |   2 |   TABLE ACCESS FULL | EMP     |      1 |     14 |     14 |00:00:00.01 |       3 |
    |   3 |   VIEW              | VW_SQ_1 |      1 |      1 |      1 |00:00:00.01 |       4 |
    |*  4 |    TABLE ACCESS FULL| DEPT    |      1 |      1 |      1 |00:00:00.01 |       4 |
    -----------------------------------------------------------------------------------------
    
       1 - access("ITEM_1"="EMP"."DEPTNO")
       4 - filter("DNAME"='ACCOUNTING')
    
    
    23 rows selected.

    可以看到 IN 与 EXIST 在CBO模式执行计划 都是一样的。   所以,我暂时得出结论--以前强调 IN , EXIST的用法是因为优化器是RBO,现在 用不用太在意 用IN 还是 EXIST。但是保守起见又去看了一下10gR2的官方文档。

    在看10g R2的文档(Performance Tuning Guide)的时候,发现下面这种说法。

    In certain circumstances, it is better to use IN rather than EXISTS. In general, if the selective predicate is in the subquery, then use IN. If the selective predicate is in the parent query, then use EXISTS.

    Sometimes, Oracle can rewrite a subquery when used with an IN clause to take advantage of selectivity specified in the subquery. This is most beneficial when the most selective filter appears in the subquery and there are indexes on the join columns. Conversely, using EXISTS is beneficial when the most selective filter is in the parent query. This allows the selective predicates in the parent query to be applied before filtering the rows against the EXISTS criteria.

    "Example 1: Using IN - Selective Filters in the Subquery" and "Example 2: Using EXISTS - Selective Predicate in the Parent" are two examples that demonstrate the benefits of IN and EXISTS. Both examples use the same schema with the following characteristics:

    • There is a unique index on the employees.employee_id field.

    • There is an index on the orders.customer_id field.

    • There is an index on the employees.department_id field.

    • The employees table has 27,000 rows.

    • The orders table has 10,000 rows.

    • The OE and HR schemas, which own these segments, were both analyzed with COMPUTE.

    Example 1: Using IN - Selective Filters in the Subquery

    This example demonstrates how rewriting a query to use IN can improve performance. This query identifies all employees who have placed orders on behalf of customer 144.

    The following SQL statement uses EXISTS:

    SELECT /* EXISTS example */
             e.employee_id, e.first_name, e.last_name, e.salary
      FROM employees e
     WHERE EXISTS (SELECT 1 FROM orders o                  /* Note 1 */
                      WHERE e.employee_id = o.sales_rep_id   /* Note 2 */
                        AND o.customer_id = 144);            /* Note 3 */
    

    Notes:

    • Note 1: This shows the line containing EXISTS.

    • Note 2: This shows the line that makes the subquery a correlated subquery.

    • Note 3: This shows the line where the correlated subqueries include the highly selective predicate customer_id = number.

    The following plan output is the execution plan (from V$SQL_PLAN) for the preceding statement. The plan requires a full table scan of the employees table, returning many rows. Each of these rows is then filtered against the orders table (through an index).

    ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
    ---- -------------------- --------------- ---------------------- --- ----------
       0 SELECT STATEMENT                                            CHO
       1  FILTER
       2   TABLE ACCESS       FULL            EMPLOYEES              ANA        155
       3   TABLE ACCESS       BY INDEX ROWID  ORDERS                 ANA          3
       4    INDEX             RANGE SCAN      ORD_CUSTOMER_IX        ANA          1
    

    Rewriting the statement using IN results in significantly fewer resources used.

    The SQL statement using IN:

    SELECT /* IN example */
             e.employee_id, e.first_name, e.last_name, e.salary
        FROM employees e
       WHERE e.employee_id IN (SELECT o.sales_rep_id         /* Note 4 */
                                 FROM orders o
                                WHERE o.customer_id = 144);  /* Note 3 */
    

    Note:

    • Note 3: This shows the line where the correlated subqueries include the highly selective predicate customer_id = number

    • Note 4: This indicates that an IN is being used. The subquery is no longer correlated, because the IN clause replaces the join in the subquery.

    The following plan output is the execution plan (from V$SQL_PLAN) for the preceding statement. The optimizer rewrites the subquery into a view, which is then joined through a unique index to the employees table. This results in a significantly better plan, because the view (that is, subquery) has a selective predicate, thus returning only a few employee_ids. These employee_ids are then used to access the employees table through the unique index.

    ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
    ---- -------------------- --------------- ---------------------- --- ----------
       0 SELECT STATEMENT                                            CHO
       1  NESTED LOOPS                                                            5
       2   VIEW                                                                   3
       3    SORT              UNIQUE                                              3
       4     TABLE ACCESS     FULL            ORDERS                 ANA          1
       5   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA          1
       6    INDEX             UNIQUE SCAN     EMP_EMP_ID_PK          ANA
    

    官方文档的意思是这样的。如果父查询中有高效的过滤条件,父查询能返回较小的结果集 那么就应该用EXIST。 如果 子查询有更佳的过滤条件,能够返回较小的结果集,就用IN。但这一般是相对OLAP系统,并且不绝对,因为统计信息等因素影响很大。

    对应这官方文档中的例子,我们自己做一下实验。

    首先是搭建环境。创建一个外层查询表 obj。 创建一个内层查询表sy (synonym)。建立相应的索引。

    in和 exist 的查询语句为

    select /*+ gather_plan_statistics */ object_name from obj where object_name in ( select synonym_name from sy where owner='CITOSADMIN');

    select /*+ gather_plan_statistics */ object_name from obj where  exists ( select 1 from sy where sy.synonym_name = obj.object_name and sy.owner='CITOSADMIN');

    这两个语句的共同点就是子查询有一个限定条件 sy.owner=' CITOSADMIN' 可以高效的返回一个很小的结果集。 按照官方文档的理论,两者不但执行方式不同,而且效率也不同。OK 开始实验

     1 SQL> create table obj as select * from dba_objects;
     2 
     3 Table created.
     4 
     5 SQL> create table sy as select * from dba_synonyms;
     6 
     7 Table created.
     8 
     9 SQL> create index sy_ind_syname on sy(synonym_name);
    10 
    11 Index created.
    12 
    13 SQL> create index obj_ind_objname on obj(object_name);
    14 
    15 Index created.
    16 
    17 SQL> create index sy_ind_own on sy(owner);
    18 
    19 Index created.

    运行in方式的查询,并查看执行计划

     1 SQL> select /*+ gather_plan_statistics */ object_name from obj where object_name in ( select synonym_name from sy where owner='CITOSADMIN');
     2 
     3 OBJECT_NAME
     4 ------------------------------------------------
     5 /eb4470db_YARD_CLUSTER_DISCHAR
     6 WHERF_RESTRICTION
     7 
     8 SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
     9 
    10 PLAN_TABLE_OUTPUT
    11 ------------------------------------------------------------------------------
    12 SQL_ID  bkrqp62y2z7fd, child number 0
    13 -------------------------------------
    14 select /*+ gather_plan_statistics */ object_name from obj where object_name in ( select
    15 synonym_name from sy where owner='CITOSADMIN')
    16 
    17 Plan hash value: 2488893939
    18 
    19 ------------------------------------------------------------------------------------------------------------
    20 | Id  | Operation                      | Name            | Starts | A-Rows |   A-Time   | Buffers | Reads  |
    21 ------------------------------------------------------------------------------------------------------------
    22 |   1 |  NESTED LOOPS                  |                 |      1 |      2 |00:00:00.01 |       8 |      3 |
    23 |   2 |   VIEW                         | VW_NSO_1        |      1 |      2 |00:00:00.01 |       3 |      1 |
    24 |   3 |    SORT UNIQUE                 |                 |      1 |      2 |00:00:00.01 |       3 |      1 |
    25 |   4 |     TABLE ACCESS BY INDEX ROWID| SY              |      1 |      2 |00:00:00.01 |       3 |      1 |
    26 |*  5 |      INDEX RANGE SCAN          | SY_IND_OWN      |      1 |      2 |00:00:00.01 |       2 |      1 |
    27 |*  6 |   INDEX RANGE SCAN             | OBJ_IND_OBJNAME |      2 |      2 |00:00:00.01 |       5 |      2 |
    28 ------------------------------------------------------------------------------------------------------------
    29 
    30 Predicate Information (identified by operation id):
    31 ---------------------------------------------------
    32 
    33    5 - access("OWNER"='CITOSADMIN')
    34    6 - access("OBJECT_NAME"="$nso_col_1")
    35 
    36 Note
    37 -----
    38    - rule based optimizer used (consider using cbo)
    39 
    40 
    41 28 rows selected.

    这里居然像tom说的那样。in 走的是 sub-query 变成view 然后连接的模式。

    再看看exist的模式

     1 SQL> select /*+ gather_plan_statistics */ object_name from obj where  exists ( select 1 from sy where sy.synonym_name = obj.object_name and sy.owner='CITOSADMIN');
     2 
     3 OBJECT_NAME
     4 ------------------------------------------------
     5 /eb4470db_YARD_CLUSTER_DISCHAR
     6 WHERF_RESTRICTION
     7 
     8 SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
     9 
    10 PLAN_TABLE_OUTPUT
    11 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    12 SQL_ID  902k3xnyr41w2, child number 0
    13 -------------------------------------
    14 select /*+ gather_plan_statistics */ object_name from obj where  exists ( select 1
    15 from sy where sy.synonym_name = obj.object_name and sy.owner='CITOSADMIN')
    16 
    17 Plan hash value: 482598837
    18 
    19 ----------------------------------------------------------------------------------------------
    20 | Id  | Operation          | Name          | Starts | A-Rows |   A-Time   | Buffers | Reads  |
    21 ----------------------------------------------------------------------------------------------
    22 |*  1 |  FILTER            |               |      1 |      2 |00:00:01.54 |     217K|    108 |
    23 |   2 |   TABLE ACCESS FULL| OBJ           |      1 |  56697 |00:00:00.06 |     787 |      0 |
    24 |   3 |   AND-EQUAL        |               |  54946 |      2 |00:00:01.36 |     217K|    108 |
    25 |*  4 |    INDEX RANGE SCAN| SY_IND_SYNAME |  54946 |  41304 |00:00:00.63 |     134K|    108 |
    26 |*  5 |    INDEX RANGE SCAN| SY_IND_OWN    |  54946 |  24488 |00:00:00.40 |   82599 |      0 |
    27 ----------------------------------------------------------------------------------------------
    28 
    29 Predicate Information (identified by operation id):
    30 ---------------------------------------------------
    31 
    32    1 - filter( IS NOT NULL)
    33    4 - access("SY"."SYNONYM_NAME"=:B1)
    34    5 - access("SY"."OWNER"='CITOSADMIN')
    35 
    36 Note
    37 -----
    38    - rule based optimizer used (consider using cbo)
    39 
    40 
    41 28 rows selected.

    好吧我败了。 前面的结论推翻了。 IN 和 EXIST在写法的选择上还是有讲究的。 这个实验跟官方文档中的说法完全一样,和tom的说法也一样。   子查询有高效的where条件的时候,IN 更快些。

    再看官方文档中关于exists 优于in的情况

    This example demonstrates how rewriting a query to use EXISTS can improve performance. This query identifies all employees from department 80 who are sales reps who have placed orders.

    The following SQL statement uses IN:

    SELECT /* IN example */
             e.employee_id, e.first_name, e.last_name, e.department_id, e.salary
        FROM employees e
       WHERE e.department_id = 80                                    /* Note 5 */
         AND e.job_id        = 'SA_REP'                              /* Note 6 */
         AND e.employee_id IN (SELECT o.sales_rep_id FROM orders o); /* Note 4 */
    

    Note:

    • Note 4: This indicates that an IN is being used. The subquery is no longer correlated, because the IN clause replaces the join in the subquery.

    • Note 5 and 6: These are the selective predicates in the parent SQL.

    The following plan output is the execution plan (from V$SQL_PLAN) for the preceding statement. The SQL statement was rewritten by the optimizer to use a view on the orders table, which requires sorting the data to return all unique employee_ids existing in the orders table. Because there is no predicate, many employee_ids are returned. The large list of resulting employee_ids are then used to access the employees table through the unique index.

    ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
    ---- -------------------- --------------- ---------------------- --- ----------
       0 SELECT STATEMENT                                            CHO
       1  NESTED LOOPS                                                          125
       2   VIEW                                                                 116
       3    SORT              UNIQUE                                            116
       4     TABLE ACCESS     FULL            ORDERS                 ANA         40
       5   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA          1
       6    INDEX             UNIQUE SCAN     EMP_EMP_ID_PK          ANA
    

    The following SQL statement uses EXISTS:

    SELECT /* EXISTS example */
             e.employee_id, e.first_name, e.last_name, e.salary
        FROM employees e
       WHERE e.department_id = 80                           /* Note 5 */
         AND e.job_id        = 'SA_REP'                     /* Note 6 */
         AND EXISTS (SELECT 1                               /* Note 1 */
                       FROM orders o
                      WHERE e.employee_id = o.sales_rep_id);  /* Note 2 */
    

    Note:

    • Note 1: This shows the line containing EXISTS.

    • Note 2: This shows the line that makes the subquery a correlated subquery.

    • Note 5 & 6:These are the selective predicates in the parent SQL.

    The following plan output is the execution plan (from V$SQL_PLAN) for the preceding statement. The cost of the plan is reduced by rewriting the SQL statement to use an EXISTS. This plan is more effective, because two indexes are used to satisfy the predicates in the parent query, thus returning only a few employee_ids. The employee_ids are then used to access the orders table through an index.

    ID OPERATION            OPTIONS         OBJECT_NAME            OPT       COST
    ---- -------------------- --------------- ---------------------- --- ----------
       0 SELECT STATEMENT                                            CHO
       1  FILTER
       2   TABLE ACCESS       BY INDEX ROWID  EMPLOYEES              ANA         98
       3    AND-EQUAL
       4     INDEX            RANGE SCAN      EMP_JOB_IX             ANA
       5     INDEX            RANGE SCAN      EMP_DEPARTMENT_IX      ANA
       6   INDEX              RANGE SCAN      ORD_SALES_REP_IX       ANA          8
    

    Note:

    An even more efficient approach is to have a concatenated index on department_id and job_id. This eliminates the need to access two indexes and reduces the resources used.
     
    参照官方文档的例子我们也实验一下:
    SQL> select /*+ gather_plan_statistics */ object_name from obj where owner='CITOSADMIN' and object_type='SYNONYM' and exists
      2  (select null from sy where obj.object_name=sy.synonym_name);
    
    OBJECT_NAME
    ------------------------------------------------
    /eb4470db_YARD_CLUSTER_DISCHAR
    WHERF_RESTRICTION
    
    Elapsed: 00:00:00.03
    SQL>
    SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------------------------------------------------------
    SQL_ID  0k96pgrx3k1u5, child number 0
    -------------------------------------
    select /*+ gather_plan_statistics */ object_name from obj where owner='CITOSADMIN' and
    object_type='SYNONYM' and exists (select null from sy where
    obj.object_name=sy.synonym_name)
    
    Plan hash value: 2625485413
    
    --------------------------------------------------------------------------------------------------
    | Id  | Operation              | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
    --------------------------------------------------------------------------------------------------
    |*  1 |  HASH JOIN SEMI        |               |      1 |     55 |      2 |00:00:00.02 |     876 |
    |*  2 |   TABLE ACCESS FULL    | OBJ           |      1 |     55 |      2 |00:00:00.01 |     786 |
    |   3 |   VIEW                 | VW_SQ_1       |      1 |  21343 |  16254 |00:00:00.01 |      90 |
    |   4 |    INDEX FAST FULL SCAN| SY_IND_SYNAME |      1 |  21343 |  16254 |00:00:00.01 |      90 |
    --------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - access("OBJ"."OBJECT_NAME"="SYNONYM_NAME")
       2 - filter(("OWNER"='CITOSADMIN' AND "OBJECT_TYPE"='SYNONYM'))
    
    
    23 rows selected.
    
    Elapsed: 00:00:00.03
    SQL>
    SQL>
    SQL>
    SQL>
    SQL>
    SQL>
    SQL>
    SQL>
    SQL>
    SQL>
    SQL>
    SQL> select /*+ gather_plan_statistics */ object_name from obj where owner='CITOSADMIN' and object_type='SYNONYM' and object_name in
      2  (select synonym_name from sy);
    
    OBJECT_NAME
    ------------------------------------------------
    /eb4470db_YARD_CLUSTER_DISCHAR
    WHERF_RESTRICTION
    
    Elapsed: 00:00:00.04
    SQL> select * from TABLE(dbms_xplan.display_cursor(null,null,'iostats last'));
    
    PLAN_TABLE_OUTPUT
    --------------------------------------------------------------------------------------------------------------------------------
    SQL_ID  dzfagp8vyh1xh, child number 0
    -------------------------------------
    select /*+ gather_plan_statistics */ object_name from obj where owner='CITOSADMIN' and
    object_type='SYNONYM' and object_name in (select synonym_name from sy)
    
    Plan hash value: 3101896092
    
    --------------------------------------------------------------------------------------------------
    | Id  | Operation              | Name          | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
    --------------------------------------------------------------------------------------------------
    |*  1 |  HASH JOIN SEMI        |               |      1 |     55 |      2 |00:00:00.02 |     876 |
    |*  2 |   TABLE ACCESS FULL    | OBJ           |      1 |     55 |      2 |00:00:00.01 |     786 |
    |   3 |   VIEW                 | VW_NSO_1      |      1 |  21343 |  16254 |00:00:00.01 |      90 |
    |   4 |    INDEX FAST FULL SCAN| SY_IND_SYNAME |      1 |  21343 |  16254 |00:00:00.01 |      90 |
    --------------------------------------------------------------------------------------------------
    
    Predicate Information (identified by operation id):
    ---------------------------------------------------
    
       1 - access("OBJECT_NAME"="$nso_col_1")
       2 - filter(("OWNER"='CITOSADMIN' AND "OBJECT_TYPE"='SYNONYM'))
    
    
    22 rows selected.
    
    Elapsed: 00:00:00.04
     这个例子中,父查询采用了较高效率的谓词条件,但是IN 和 EXISTS的指向计划和效率是一样的,没有像官网上说的那样。
     
     
     
    ok,现在的结论就是,如果主查询的选择性很好,可以返回一个较小的结果集,就可以考虑用 exists, 如果子查询的选择性很好,可以返回一个较小的结果集,就可以考虑用in。但是上述说法不绝对,在实际环境中,还是都用下比较比较。。
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    10、xsl中import用法
    09、xsl中输出对应的列和值
    08、xsl中操作子节点带循环输出
    07、xsl中操作子节点
    06、xsl中choose进行多条件选择
    05、xsl中IF的用法
    04、xsl中对字段进行排序
    03、xsl中添加筛选条件
    02、xsl的for循环输出
    01、xsl样式表用网页输出
  • 原文地址:https://www.cnblogs.com/kramer/p/3012411.html
Copyright © 2020-2023  润新知