Oracle mechanics

07.08.2012

Oracle 11g: стоимость операции FILTER и OR-Expansion

Filed under: bugs,CBO,heuristics,hints,Oracle — Игорь Усольцев @ 00:29
Tags: , ,

Практическая проблема выглядела следующим образом: при выполнении дизъюнктивного (с использованием OR в разделе WHERE) DML оператора DELETE Oracle 11.1 выбирает формально недорогой, но на практике выполняющийся часами план:

11.1.0.7.@ SQL> explain plan for
  2  DELETE
  3   FROM "T1"
  4   WHERE min_unimported_period_time = max_period_time
  5     AND (source_uri NOT IN
  6           (
  7            SELECT DISTINCT
  8            source_uri
  9             FROM "T2" t
 10            WHERE period_time >= TO_DATE('2012-07-30 06:41:35', 'YYYY-MM-DD HH24:MI:SS')
 11           )
 12          OR
 13          max_period_time < TO_DATE('2012-08-01 00:41:35', 'YYYY-MM-DD HH24:MI:SS')
 14          )
 15  /

---------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
---------------------------------------------------------------------------------------------------------------
|   0 | DELETE STATEMENT                     |        |   827 |   108K|   615   (1)| 00:00:12 |       |       |
|   1 |  DELETE                              | T1     |       |       |            |          |       |       |
|*  2 |   FILTER                             |        |       |       |            |          |       |       |
|*  3 |    TABLE ACCESS FULL                 | T1     | 16531 |  2163K|   615   (1)| 00:00:12 |       |       |
|   4 |    PARTITION RANGE ITERATOR          |        |  4302 |   306K| 46284   (1)| 00:13:54 |   181 |   189 |
|*  5 |     TABLE ACCESS BY LOCAL INDEX ROWID| T2     |  4302 |   306K| 46284   (1)| 00:13:54 |   181 |   189 |
|*  6 |      INDEX SKIP SCAN                 | T2_IDX |  4602 |       | 41596   (1)| 00:12:29 |   181 |   189 |
---------------------------------------------------------------------------------------------------------------

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

   2 - filter("MAX_PERIOD_TIME"<TO_DATE(' 2012-08-01 00:41:35', 'syyyy-mm-dd hh24:mi:ss') OR  NOT EXISTS (SELECT 0 FROM
              "T2" "T" WHERE "PERIOD_TIME">=TO_DATE(' 2012-07-30 06:41:35', 'syyyy-mm-dd hh24:mi:ss') AND
              "SOURCE_URI"=:B1))
   3 - filter("MIN_UNIMPORTED_PERIOD_TIME"="MAX_PERIOD_TIME")
   5 - filter("PERIOD_TIME">=TO_DATE(' 2012-07-30 06:41:35', 'syyyy-mm-dd hh24:mi:ss'))
   6 - access("SOURCE_URI"=:B1)
       filter("SOURCE_URI"=:B1)

Почти такой же план (за исключением замены INDEX SKIP SCAN на TABLE ACCESS FULL, что, однако, не меняет итоговой стоимости и  наводит на странную мысль, что в рассматриваемом случае метод доступа к строкам таблицы T2 не имеет значения) выбирается и для SELECT:

11.1.0.7.@ SQL> explain plan for
  2  select *
  3   FROM "T1"
  4   WHERE min_unimported_period_time = max_period_time
  5     AND (source_uri NOT IN
  6           (
  7            SELECT DISTINCT
  8            source_uri
  9             FROM "T2" t
 10            WHERE period_time >= TO_DATE('2012-07-30 06:41:35', 'YYYY-MM-DD HH24:MI:SS')
 11           )
 12          OR
 13          max_period_time < TO_DATE('2012-08-01 00:41:35', 'YYYY-MM-DD HH24:MI:SS')
 14          )
 15  /

--------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |      |   827 |   108K|   615   (1)| 00:00:12 |       |       |
|*  1 |  FILTER                   |      |       |       |            |          |       |       |
|*  2 |   TABLE ACCESS FULL       | T1   | 16531 |  2163K|   615   (1)| 00:00:12 |       |       |
|   3 |   PARTITION RANGE ITERATOR|      |     2 |   146 |   111   (0)| 00:00:03 |   181 |   189 |
|*  4 |    TABLE ACCESS FULL      | T2   |     2 |   146 |   111   (0)| 00:00:03 |   181 |   189 |
--------------------------------------------------------------------------------------------------

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

   1 - filter("MAX_PERIOD_TIME"<TO_DATE(' 2012-08-01 00:41:35', 'syyyy-mm-dd hh24:mi:ss') OR  NOT EXISTS (SELECT 0
              FROM "T2" "T" WHERE "SOURCE_URI"=:B1 AND "PERIOD_TIME">=TO_DATE(' 2012-07-30
              06:41:35', 'syyyy-mm-dd hh24:mi:ss')))
   2 - filter("MIN_UNIMPORTED_PERIOD_TIME"="MAX_PERIOD_TIME")
   4 - filter("SOURCE_URI"=:B1 AND "PERIOD_TIME">=TO_DATE(' 2012-07-30 06:41:35', 'syyyy-mm-dd hh24:mi:ss'))

Учитывая, что операция FILTER в последнем запросе выполняет простой NESTED LOOPS (правда, для каждой строки таблицы T1, удовлетворяющей условию 2 — filter(«MIN_UNIMPORTED_PERIOD_TIME»=»MAX_PERIOD_TIME») выполняется полное сканирование части партиций T2) сравнительно необычно выглядит высокая стоимость запроса с одним оставленным условием, использующего более эффективную и быструю (в этом случае) операцию HASH JOIN ANTI:

11.1.0.7.@ SQL> explain plan for
  2  select *
  3   FROM "T1"
  4   WHERE min_unimported_period_time = max_period_time
  5     AND source_uri NOT IN
  6           (
  7            SELECT DISTINCT
  8            source_uri
  9             FROM "T2" t
 10            WHERE period_time >= TO_DATE('2012-07-30 06:41:35', 'YYYY-MM-DD HH24:MI:SS')
 11           )
 12  /

----------------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |      | 16528 |  3341K|       |   313K  (1)| 01:33:57 |       |       |
|*  1 |  HASH JOIN ANTI           |      | 16528 |  3341K|  2368K|   313K  (1)| 01:33:57 |       |       |
|*  2 |   TABLE ACCESS FULL       | T1   | 16531 |  2163K|       |   615   (1)| 00:00:12 |       |       |
|   3 |   PARTITION RANGE ITERATOR|      |    28M|  1974M|       |   235K  (1)| 01:10:33 |   181 |   189 |
|*  4 |    TABLE ACCESS FULL      | T2   |    28M|  1974M|       |   235K  (1)| 01:10:33 |   181 |   189 |
----------------------------------------------------------------------------------------------------------

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

   1 - access("SOURCE_URI"="SOURCE_URI")
   2 - filter("MIN_UNIMPORTED_PERIOD_TIME"="MAX_PERIOD_TIME")
   4 - filter("PERIOD_TIME">=TO_DATE(' 2012-07-30 06:41:35', 'syyyy-mm-dd hh24:mi:ss'))

Если в запросе оставить только второе условие – проблема становится более понятной:

11.1.0.7.@ SQL> explain plan for
  2  select *
  3    FROM "T1"
  4   WHERE min_unimported_period_time = max_period_time
  5     AND max_period_time < TO_DATE('2012-08-01 00:41:35', 'YYYY-MM-DD HH24:MI:SS')
  6  /

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |   134 |   615   (1)| 00:00:12 |
|*  1 |  TABLE ACCESS FULL| T1   |     1 |   134 |   615   (1)| 00:00:12 |
--------------------------------------------------------------------------

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

   1 - filter("MAX_PERIOD_TIME"<TO_DATE(' 2012-08-01 00:41:35', 'syyyy-mm-dd hh24:mi:ss')
              AND "MIN_UNIMPORTED_PERIOD_TIME"<TO_DATE(' 2012-08-01 00:41:35', 'syyyy-mm-dd hh24:mi:ss')
              AND "MIN_UNIMPORTED_PERIOD_TIME"="MAX_PERIOD_TIME")

– проблема в том, что для оценки стоимости операции FILTER в первоначальном запросе:

--------------------------------------------------------------------------------------------------
| Id  | Operation                 | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT          |      |   827 |   108K|   615   (1)| 00:00:12 |       |       |
|*  1 |  FILTER                   |      |       |       |            |       |          |       |
|*  2 |   TABLE ACCESS FULL       | T1   | 16531 |  2163K|   615   (1)| 00:00:12 |       |       |
|   3 |   PARTITION RANGE ITERATOR|      |     2 |   146 |   111   (0)| 00:00:03 |   181 |   189 |
|*  4 |    TABLE ACCESS FULL      | T2   |     2 |   146 |   111   (0)| 00:00:03 |   181 |   189 |
--------------------------------------------------------------------------------------------------

– оптимизатор просто использовал стоимость операции TABLE ACCESS FULL таблицы T1, т.е. стоимость ограниченного списком партиций картезианского произведения с таблицей T2 не учтена совсем!
Важно, что запросы либо с первым, либо со вторым условием по отдельности выполняются за секунды, в то время как первоначальный дизъюнктивный запрос с обоими условиями, объединёнными OR – выполнялся часами, производя фактически катезианзкое произведение под видом операции FILTER

Поняв, что Oracle 11.1 достаточно упрощённо оценивает стоимость операции FILTER в дизъюнктивных запросах, как можно исправить ситуацию?

1) Выполнять вместо одного два DML с отдельными условиями т.к. к сожалению, операторы UNION [ALL] предназначены только для SELECT

2) Преобразовать запрос с помощью оператора MINUS. При этом, несмотря на большие объёмы чтений запрос, используя быстрый HASH JOIN, выполняется за вполне разумное время:

11.1.0.7.@ SQL> select *
  2   FROM "T1"
  3   WHERE
  4         source_uri NOT IN
  5           (
  6            SELECT
  7            source_uri
  8             FROM "T2" t
  9            WHERE period_time >= TO_DATE('2012-07-30 06:41:35', 'YYYY-MM-DD HH24:MI:SS')
 10            MINUS
 11            select source_uri FROM "T1" where max_period_time < TO_DATE('2012-08-01 00:41:35', 'YYYY-MM-DD HH24:MI:SS')
 12           )
 13  /

no rows selected

Elapsed: 00:03:59.46

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          | 92564 |    57M|       |   908K  (1)| 04:32:27 |       |       |
|*  1 |  HASH JOIN ANTI              |          | 92564 |    57M|    12M|   908K  (1)| 04:32:27 |       |       |
|   2 |   TABLE ACCESS FULL          | T1       | 92579 |    11M|       |   615   (1)| 00:00:12 |       |       |
|   3 |   VIEW                       | VW_NSO_1 |    28M|    13G|       |   429K  (1)| 02:08:55 |       |       |
|   4 |    MINUS                     |          |       |       |       |         |             |       |       |
|   5 |     SORT UNIQUE              |          |    28M|  1974M|  2278M|         |             |       |       |
|   6 |      PARTITION RANGE ITERATOR|          |    28M|  1974M|       |   235K  (1)| 01:10:33 |   181 |   189 |
|*  7 |       TABLE ACCESS FULL      | T2       |    28M|  1974M|       |   235K  (1)| 01:10:33 |   181 |   189 |
|   8 |     SORT UNIQUE              |          |     1 |    73 |       |         |             |       |       |
|*  9 |      TABLE ACCESS FULL       | T1       |     1 |    73 |       |   615   (1)| 00:00:12 |       |       |
-----------------------------------------------------------------------------------------------------------------

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

   1 - access("SOURCE_URI"="SOURCE_URI")
   7 - filter("PERIOD_TIME">=TO_DATE(' 2012-07-30 06:41:35', 'syyyy-mm-dd hh24:mi:ss'))
   9 - filter("MAX_PERIOD_TIME"<TO_DATE(' 2012-08-01 00:41:35', 'syyyy-mm-dd hh24:mi:ss'))

Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
     686178  consistent gets
     471665  physical reads
   14770256  redo size -- таблицы постоянно и активно модифицируются открытыми транзакциями: CLEANOUT

3) Наиболее правильным с моей точки зрения решением было использование механизма OR-Expansion, использование которого по причине ошибочной оценки стоимости операции FILTER рекомендуется форсировать с помощью подсказки /*+ USE_CONCAT*/

К сожалению, до версии 11.2 сделать это невозможно – подсказка USE_CONCAT не отрабатывает, в трейсе 10053 можно найти упоминание о неких внутренних подсказках NO_EXPAND, имеющих прямо противоположную направленность, и недостающих индексах:

Or-Expansion validity checks failed on query block SEL$2 (#0) because NO_EXPAND hint
...
Trying or-Expansion on query block SEL$1 (#0)
Or-expansion bypassed: No index driver found.

Работоспособность подсказка /*+ USE_CONCAT*/  можно проверить на примере для тестовой схемы SCOTT в версиях 11.1 и 11.2:

SQL> -- сделаем индекс для поиска по NULL-значению EMP.MGR по рецепту Т.Кайта
SQL> create index IDX_MGR on scott.EMP (MGR, 0) tablespace USERS;

Index created.

11.1.0.7.@ SQL> select--+ USE_CONCAT(OR_PREDICATES(1))
  2   *
  3   FROM scott.emp e
  4   WHERE e.empno NOT IN
  5           (
  6            SELECT e.empno
  7             FROM scott.emp e
  8            WHERE e.sal > 800
  9           )
 10          OR e.mgr is NULL
 11  /

---------------------------------------------------------------------------------------
| Id  | Operation                    | Name   | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |        |     2 |    78 |    10   (0)| 00:00:01 | -- никаких OR-Expansion, тот же неправильный Cost для FILTER :(
|*  1 |  FILTER                      |        |       |       |            |          |
|   2 |   TABLE ACCESS FULL          | EMP    |    14 |   546 |    10   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS BY INDEX ROWID| EMP    |     1 |     8 |     1   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | PK_EMP |     1 |       |     0   (0)| 00:00:01 |
---------------------------------------------------------------------------------------

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

   1 - filter("SYS_ALIAS_1"."MGR" IS NULL OR  NOT EXISTS (SELECT 0 FROM
              "SCOTT"."EMP" "E" WHERE "E"."EMPNO"=:B1 AND "E"."SAL">800))
   3 - filter("E"."SAL">800)
   4 - access("E"."EMPNO"=:B1)

Для 11.2 без подсказки выбирается (по-прежнему) неточно подсчитанный план с операцией FILTER:

11.2.0.3.OEBS2@SYSTEM SQL> select
  2  *
  3  FROM scott.emp e
  4  WHERE e.empno NOT IN
  5          (
  6           SELECT e.empno
  7            FROM scott.emp e
  8           WHERE e.sal > 800
  9          )
 10         OR e.mgr is NULL
 11  /

---------------------------------------------------------------------------------------------
| Id  | Operation                    | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
---------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |              |     2 |    76 |     7   (0)| 00:00:01 |
|*  1 |  FILTER                      |              |       |       |            |          |
|   2 |   TABLE ACCESS FULL          | EMP          |    14 |   532 |     7   (0)| 00:00:01 |
|*  3 |   TABLE ACCESS BY INDEX ROWID| EMP          |     1 |     8 |     1   (0)| 00:00:01 |
|*  4 |    INDEX UNIQUE SCAN         | SYS_C0093796 |     1 |       |     0   (0)| 00:00:01 |
---------------------------------------------------------------------------------------------

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

   1 - filter("E"."MGR" IS NULL OR  NOT EXISTS (SELECT 0 FROM "SCOTT"."EMP" "E"
              WHERE "E"."EMPNO"=:B1 AND "E"."SAL">800))
   3 - filter("E"."SAL">800)
   4 - access("E"."EMPNO"=:B1)

Statistics
----------------------------------------------------------
...
         44  consistent gets
...
          2  rows processed

Подсказка отрабатывает, но только с недокументированным параметром /*+ USE_CONCAT( OR_PREDICATES(1) ) */ :

11.2.0.3.OEBS2@SYSTEM SQL> select--+ USE_CONCAT(OR_PREDICATES(1))
  2  *
  3  FROM scott.emp e
  4  WHERE e.empno NOT IN
  5          (
  6           SELECT e.empno
  7            FROM scott.emp e
  8           WHERE e.sal > 800
  9          )
 10         OR e.mgr is NULL
 11  /

Elapsed: 00:00:00.01

Execution Plan
----------------------------------------------------------
Plan hash value: 778239593

----------------------------------------------------------------------------------------------
| Id  | Operation                     | Name         | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |              |    14 |   532 |    10   (0)| 00:00:01 |
|   1 |  CONCATENATION                |              |       |       |            |          |
|   2 |   TABLE ACCESS BY INDEX ROWID | EMP          |     1 |    38 |     2   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN           | IDX_MGR      |     1 |       |     1   (0)| 00:00:01 |
|*  4 |   FILTER                      |              |       |       |            |          |
|*  5 |    TABLE ACCESS FULL          | EMP          |    13 |   494 |     7   (0)| 00:00:01 |
|*  6 |    TABLE ACCESS BY INDEX ROWID| EMP          |     1 |     8 |     1   (0)| 00:00:01 |
|*  7 |     INDEX UNIQUE SCAN         | SYS_C0093796 |     1 |       |     0   (0)| 00:00:01 |
----------------------------------------------------------------------------------------------

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

   3 - access("E"."MGR" IS NULL)
   4 - filter( NOT EXISTS (SELECT 0 FROM "SCOTT"."EMP" "E" WHERE "E"."EMPNO"=:B1 AND
              "E"."SAL">800))
   5 - filter(LNNVL("E"."MGR" IS NULL))
   6 - filter("E"."SAL">800)
   7 - access("E"."EMPNO"=:B1)

Statistics
----------------------------------------------------------
          8  recursive calls
          0  db block gets
         46  consistent gets
          0  physical reads
          0  redo size
...
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed

– и судя по близким значениям consistent gets стоимость во втором случае подсчитана более адекватно

По теме:

  • Optimizer Transformations: OR Expansion — описание улучшения механизма в 11.2, упоминается улучшения в обработке картезианских произведений, отражаемых в виде другой операции — NESTED LOOPS

11 комментариев »

  1. Основная проблема конкатенации с in/not in/exists/not exists в одной из веток в том, что в случае даже если разложит в конкатенацию, то все равно в блоке сin/exists будет fts, т.к. оптимизатор еще не умеет менять порядок доступа к таблице в другой ветке конкатенации(то есть целевая таблица будет первой). Кроме того конкатенация работает исключительно как конкатенация — то есть набор_1 union all (набор_2 minus набор_1), а следственно, в другой ветке придется отсекать те строки что попали в первую. Гораздо лучше было, если бы умел работать как union, то немного сложнее — чтобы не отсечь нужные дубликаты(те которые должны быть согласно предикатам)

    комментарий от Саян Малакшинов — 07.09.2012 @ 02:31 | Ответить

    • >> Основная проблема конкатенации с in/not in/exists/not exists в одной из веток в том, что в случае даже если разложит в конкатенацию, то все равно в блоке сin/exists будет fts, т.к. оптимизатор еще не умеет менять порядок доступа к таблице в другой ветке конкатенации(то есть целевая таблица будет первой)

      необязательно FTS и необязательно сохраняется порядок:

      если в предыдущем примере добавить индекс, можно получить сгенерированный оптимизатором план, в котором запрос S2 будет выполняться в первую очередь, что подтверждает автоматически сгенерированный хинт PUSH_SUBQ(@»S2″) и статистика выполнения, из которой видно, что на шаге #3 INDEX FULL SCAN было получена всего одна строка:

      11.2.0.3.ORCL112@SCOTT SQL> create index IDX_MGR2 on scott.EMP (MGR, empno);
      
      Index created.
      
      SQL> select --+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
        2   e.empno
        3    FROM scott.emp e
        4   WHERE e.empno NOT IN (SELECT /*+ qb_name(S2)*/
        5                          e.empno
        6                           FROM scott.emp e
        7                          WHERE e.sal > 800)
        8      OR e.mgr is NULL
        9  /
      
           EMPNO
      ----------
            7839
            7369
      
      SQL> SELECT * FROM TABLE(dbms_xplan.display_cursor('','','all allstats advanced +alias +outline +note +parallel +remote -projection -peeked_binds +predicate last'));
      
      ------------------------------------------------------------------------------------------------------------------------------------
      | Id  | Operation                     | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
      ------------------------------------------------------------------------------------------------------------------------------------
      |   0 | SELECT STATEMENT              |          |      1 |        |       |     3 (100)|          |      2 |00:00:00.01 |      28 |
      |   1 |  CONCATENATION                |          |      1 |        |       |            |          |      2 |00:00:00.01 |      28 |
      |*  2 |   INDEX RANGE SCAN            | IDX_MGR2 |      1 |      1 |     8 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       1 |
      |*  3 |   INDEX FULL SCAN             | IDX_MGR2 |      1 |      1 |     8 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |      27 |
      |*  4 |    TABLE ACCESS BY INDEX ROWID| EMP      |     13 |      1 |     8 |     1   (0)| 00:00:01 |     12 |00:00:00.01 |      26 |
      |*  5 |     INDEX UNIQUE SCAN         | PK_EMP   |     13 |      1 |       |     0   (0)|          |     13 |00:00:00.01 |      13 |
      ------------------------------------------------------------------------------------------------------------------------------------
      
      Query Block Name / Object Alias (identified by operation id):
      -------------------------------------------------------------
      
         1 - S1
         2 - S1_1 / E@S1
         3 - S1_2 / E@S1_2
         4 - S2   / E@S2
         5 - S2   / E@S2
      
      Outline Data
      -------------
      
        /*+
            BEGIN_OUTLINE_DATA
            IGNORE_OPTIM_EMBEDDED_HINTS
            OPTIMIZER_FEATURES_ENABLE('11.2.0.3')
            DB_VERSION('11.2.0.3')
            ALL_ROWS
            OUTLINE_LEAF(@"S2")
            OUTLINE_LEAF(@"S1")
            OUTLINE_LEAF(@"S1_1")
            USE_CONCAT(@"S1" OR_PREDICATES(1))
            OUTLINE_LEAF(@"S1_2")
            OUTLINE(@"S2")
            OUTLINE(@"S1")
            INDEX(@"S1_1" "E"@"S1" ("EMP"."MGR" "EMP"."EMPNO"))
            INDEX(@"S1_2" "E"@"S1_2" ("EMP"."MGR" "EMP"."EMPNO"))
            PUSH_SUBQ(@"S2")                                      -- ****************************
            INDEX_RS_ASC(@"S2" "E"@"S2" ("EMP"."EMPNO"))
            END_OUTLINE_DATA
        */
      
      Predicate Information (identified by operation id):
      ---------------------------------------------------
      
         2 - access("E"."MGR" IS NULL)
         3 - filter((LNNVL("E"."MGR" IS NULL) AND  IS NULL))
         4 - filter("E"."SAL">800)
         5 - access("E"."EMPNO"=:B1)

      интересно, что фильтр 3 — filter((LNNVL(«E».»MGR» IS NULL) AND IS NULL)) именно в таком виде хранится в V$SQL_PLAN, полностью отражается в PLAN_TABLE:

      SQL> explain plan for
        2  select --+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
      ...
       10  /
      
      Explained.
      
      SQL> select * from table(dbms_xplan.display());
      
      PLAN_TABLE_OUTPUT
      ------------------------------------------------------------------------------------------
      Plan hash value: 3643328025
      
      ------------------------------------------------------------------------------------------
      | Id  | Operation                     | Name     | Rows  | Bytes | Cost (%CPU)| Time     |
      ------------------------------------------------------------------------------------------
      |   0 | SELECT STATEMENT              |          |    14 |   112 |     3   (0)| 00:00:01 |
      |   1 |  CONCATENATION                |          |       |       |            |          |
      |*  2 |   INDEX RANGE SCAN            | IDX_MGR2 |     1 |     8 |     1   (0)| 00:00:01 |
      |*  3 |   INDEX FULL SCAN             | IDX_MGR2 |     1 |     8 |     1   (0)| 00:00:01 |
      |*  4 |    TABLE ACCESS BY INDEX ROWID| EMP      |     1 |     8 |     1   (0)| 00:00:01 |
      |*  5 |     INDEX UNIQUE SCAN         | PK_EMP   |     1 |       |     0   (0)| 00:00:01 |
      ------------------------------------------------------------------------------------------
      
      Predicate Information (identified by operation id):
      ---------------------------------------------------
      
         2 - access("E"."MGR" IS NULL)
         3 - filter(LNNVL("E"."MGR" IS NULL) AND  NOT EXISTS (SELECT /*+ QB_NAME ("S2")
                    */ 0 FROM "SCOTT"."EMP" "E" WHERE "E"."EMPNO"=:B1 AND "E"."SAL">800))
         4 - filter("E"."SAL">800)
         5 - access("E"."EMPNO"=:B1)

      INDEX FULL SCAN на шаге #3 используется из-за трудности индексации функции LNNVL, по моим наблюдениям

      комментарий от Igor Usoltsev — 08.09.2012 @ 21:02 | Ответить

      • Боюсь, тут не совсем показательный пример и какая-то ошибка в трассировке. Вот попробуйте на несколько измененном примере:

        create table scott.emp2 
        as
        select * from scott.emp
        /
        select--+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
         e.empno
        FROM scott.emp e
        WHERE e.empno NOT IN (SELECT /*+ qb_name(S2)*/
                                e2.empno
                              FROM scott.emp e2
                              WHERE e2.sal > 800)
            OR e.mgr is NULL
        ;
        

        В этом случае без фулскана по индексу нельзя достать значения, которые не в списке из emp2, хотя все получите тоже самое.

        комментарий от Саян Малакшинов — 08.09.2012 @ 23:34 | Ответить

        • в своём последнем скрипте добавил создание индекса IDX_MGR2 — сорри, упустил

          я правильно понимаю запрос из вашего примера должен так выглядеть — EMP2 в подзапросе?

          select --+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
           e.empno
            FROM scott.emp e
           WHERE e.empno NOT IN (SELECT /*+ qb_name(S2)*/
                                  e2.empno
                                   FROM scott.emp2 e2
                                  WHERE e2.sal > 800)
              OR e.mgr is NULL

          комментарий от Igor Usoltsev — 09.09.2012 @ 00:57 | Ответить

        • согласен, со статистикой выполнения я погорячился
          при этом непонятным остаётся предазначение PUSH_SUBQ(@»S2″)

          комментарий от Igor Usoltsev — 09.09.2012 @ 01:33 | Ответить

          • Я догадался какой индекс по

             INDEX(@"S1_1" "E"@"S1" ("EMP"."MGR" "EMP"."EMPNO"))
            

            По поводу трассировки: честно говоря, я и сам затрудняюсь с этим. Выходит, что сначала вычитываются все строки из emp2, а затем при проходе по IDX_MGR2 они отсекаются как обычным фильтром. Но тем не менее, непонятно, почему так врет насчет A-Rows.

            На всякий случай пример:

            DB11G/XTENDER> select--+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
              2   e.empno
              3  FROM scott.emp e
              4  WHERE e.empno NOT IN (SELECT /*+ qb_name(S2)*/
              5                          e2.empno
              6                        FROM scott.emp2 e2
              7                        WHERE e2.sal > 800)
              8      OR e.mgr is NULL;
            
                 EMPNO
            ----------
                  7839
                  7369
            
            2 rows selected.
            
            Elapsed: 00:00:00.00
            DB11G/XTENDER> SELECT * FROM TABLE(dbms_xplan.display_cursor('','','all allstats advanced +alias +outline +note +parallel +remote -projection -peeked_
            binds +predicate last'));
            
            PLAN_TABLE_OUTPUT
            ------------------------------------------------------------------------------------------------------------------------------------------------------
            
            SQL_ID  0gy4522pbsc55, child number 0
            -------------------------------------
            select--+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1)
            GATHER_PLAN_STATISTICS  e.empno FROM scott.emp e WHERE e.empno NOT IN
            (SELECT /*+ qb_name(S2)*/                         e2.empno
                     FROM scott.emp2 e2                       WHERE e2.sal > 800)
              OR e.mgr is NULL
            
            Plan hash value: 3021982181
            
            --------------------------------------------------------------------------------------------------------------------------
            | Id  | Operation           | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
            --------------------------------------------------------------------------------------------------------------------------
            |   0 | SELECT STATEMENT    |          |      1 |        |       |     5 (100)|          |      2 |00:00:00.01 |      41 |
            |   1 |  CONCATENATION      |          |      1 |        |       |            |          |      2 |00:00:00.01 |      41 |
            |*  2 |   INDEX RANGE SCAN  | IDX_MGR2 |      1 |      1 |     8 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       1 |
            |*  3 |   INDEX FULL SCAN   | IDX_MGR2 |      1 |      1 |     8 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |      40 |
            |*  4 |    TABLE ACCESS FULL| EMP2     |     13 |      1 |     8 |     3   (0)| 00:00:01 |     12 |00:00:00.01 |      39 |
            --------------------------------------------------------------------------------------------------------------------------
            
            Query Block Name / Object Alias (identified by operation id):
            -------------------------------------------------------------
            
               1 - S1
               2 - S1_1 / E@S1
               3 - S1_2 / SYS_ALIAS_1@S1_2
               4 - S2   / E2@S2
            
            Outline Data
            -------------
            
              /*+
                  BEGIN_OUTLINE_DATA
                  IGNORE_OPTIM_EMBEDDED_HINTS
                  OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
                  DB_VERSION('11.2.0.1')
                  ALL_ROWS
                  OUTLINE_LEAF(@"S2")
                  OUTLINE_LEAF(@"S1")
                  OUTLINE_LEAF(@"S1_1")
                  USE_CONCAT(@"S1" OR_PREDICATES(1))
                  OUTLINE_LEAF(@"S1_2")
                  OUTLINE(@"S2")
                  OUTLINE(@"S1")
                  INDEX(@"S1_1" "E"@"S1" ("EMP"."MGR" "EMP"."EMPNO"))
                  INDEX(@"S1_2" "SYS_ALIAS_1"@"S1_2" ("EMP"."MGR" "EMP"."EMPNO"))
                  PUSH_SUBQ(@"S2")
                  FULL(@"S2" "E2"@"S2")
                  END_OUTLINE_DATA
              */
            
            Predicate Information (identified by operation id):
            ---------------------------------------------------
            
               2 - access("E"."MGR" IS NULL)
               3 - filter((LNNVL("E"."MGR" IS NULL) AND  IS NULL))
               4 - filter(("E2"."SAL">800 AND LNNVL("E2"."EMPNO"<>:B1)))
            
            
            58 rows selected.
            

            Из трассы:

            select--+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
             e.empno
            FROM scott.emp e
            WHERE e.empno NOT IN (SELECT /*+ qb_name(S2)*/
                                    e2.empno
                                  FROM scott.emp2 e2
                                  WHERE e2.sal > 800)
                OR e.mgr is NULL
            
            call     count       cpu    elapsed       disk      query    current        rows
            ------- ------  -------- ---------- ---------- ---------- ----------  ----------
            Parse        1      0.09       0.09          0          3          0           0
            Execute      1      0.00       0.00          0          0          0           0
            Fetch        2      0.00       0.00          0         41          0           2
            ------- ------  -------- ---------- ---------- ---------- ----------  ----------
            total        4      0.09       0.09          0         44          0           2
            
            Misses in library cache during parse: 1
            Optimizer mode: ALL_ROWS
            Parsing user id: 92  
            
            Rows     Row Source Operation
            -------  ---------------------------------------------------
                  2  CONCATENATION  (cr=41 pr=0 pw=0 time=0 us)
                  1   INDEX RANGE SCAN IDX_MGR2 (cr=1 pr=0 pw=0 time=0 us cost=1 size=8 card=1)(object id 109659)
                  1   INDEX FULL SCAN IDX_MGR2 (cr=40 pr=0 pw=0 time=0 us cost=1 size=8 card=1)(object id 109659)
                 12    TABLE ACCESS FULL EMP2 (cr=39 pr=0 pw=0 time=0 us cost=2 size=312 card=12)
            

            Хотя и очевидно, что по IDX_MGR2 будет настоящий фулскан, т.к. index range scan быть не может из-за отсутстсвия необходимых значений.

            комментарий от Саян Малакшинов — 09.09.2012 @ 01:48 | Ответить

            • Все ясно, это обычный фильтр, просто A-ROWS врет, проверка простая:

              1. создаем функцию с выводом и возвратом своего же аргумента(хотя проще как Кайт делать функцию с инкрементом пакетной переменной, но у меня fire всегда под рукой):

              create or replace function fire(p in number) return number is
              begin
                dbms_output.put_line('fired');
                return p;
              end fire;
              

              2. выполняем запрос с ней в предикате:

              select--+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
               e.empno
              FROM scott.emp e
              WHERE fire(e.empno) NOT IN (SELECT /*+ qb_name(S2)*/
                                      e2.empno
                                    FROM scott.emp2 e2
                                    WHERE e2.sal > 800)
                  OR e.mgr is NULL;
              

              Вот что выйдет:

              Elapsed: 00:00:00.09
              DB11G/XTENDER> set serverout on
              DB11G/XTENDER> select--+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1) GATHER_PLAN_STATISTICS
                2   e.empno
                3  FROM scott.emp e
                4  WHERE fire(e.empno) NOT IN (SELECT /*+ qb_name(S2)*/
                5                          e2.empno
                6                        FROM scott.emp2 e2
                7                        WHERE e2.sal > 800)
                8      OR e.mgr is NULL;
              
                   EMPNO
              ----------
                    7839
                    7369
              
              2 rows selected.
              
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              fired
              Elapsed: 00:00:00.12
              
              DB11G/XTENDER> SELECT * FROM TABLE(dbms_xplan.display_cursor('7u0k0yq3fkr9b',0,'all allstats advanced +alias +outline +note +parallel +remote -project
              ion -peeked_binds +predicate last'));
              
              PLAN_TABLE_OUTPUT
              ------------------------------------------------------------------------------------------------------------------------------------------------------
              
              SQL_ID  7u0k0yq3fkr9b, child number 0
              -------------------------------------
              select--+ USE_CONCAT(OR_PREDICATES(1)) qb_name(S1)
              GATHER_PLAN_STATISTICS  e.empno FROM scott.emp e WHERE fire(e.empno)
              NOT IN (SELECT /*+ qb_name(S2)*/                         e2.empno
                              FROM scott.emp2 e2                       WHERE e2.sal >
              800)     OR e.mgr is NULL
              
              Plan hash value: 3021982181
              
              --------------------------------------------------------------------------------------------------------------------------
              | Id  | Operation           | Name     | Starts | E-Rows |E-Bytes| Cost (%CPU)| E-Time   | A-Rows |   A-Time   | Buffers |
              --------------------------------------------------------------------------------------------------------------------------
              |   0 | SELECT STATEMENT    |          |      1 |        |       |     5 (100)|          |      2 |00:00:00.01 |      41 |
              |   1 |  CONCATENATION      |          |      1 |        |       |            |          |      2 |00:00:00.01 |      41 |
              |*  2 |   INDEX RANGE SCAN  | IDX_MGR2 |      1 |      1 |     8 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |       1 |
              |*  3 |   INDEX FULL SCAN   | IDX_MGR2 |      1 |      1 |     8 |     1   (0)| 00:00:01 |      1 |00:00:00.01 |      40 |
              |*  4 |    TABLE ACCESS FULL| EMP2     |     13 |      1 |     8 |     3   (0)| 00:00:01 |     12 |00:00:00.01 |      39 |
              --------------------------------------------------------------------------------------------------------------------------
              
              Query Block Name / Object Alias (identified by operation id):
              -------------------------------------------------------------
              
                 1 - S1
                 2 - S1_1 / E@S1
                 3 - S1_2 / SYS_ALIAS_1@S1_2
                 4 - S2   / E2@S2
              
              Outline Data
              -------------
              
                /*+
                    BEGIN_OUTLINE_DATA
                    IGNORE_OPTIM_EMBEDDED_HINTS
                    OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
                    DB_VERSION('11.2.0.1')
                    ALL_ROWS
              
              PLAN_TABLE_OUTPUT
              ------------------------------------------------------------------------------------------------------------------------------------------------------
              
                    OUTLINE_LEAF(@"S2")
                    OUTLINE_LEAF(@"S1")
                    OUTLINE_LEAF(@"S1_1")
                    USE_CONCAT(@"S1" OR_PREDICATES(1))
                    OUTLINE_LEAF(@"S1_2")
                    OUTLINE(@"S2")
                    OUTLINE(@"S1")
                    INDEX(@"S1_1" "E"@"S1" ("EMP"."MGR" "EMP"."EMPNO"))
                    INDEX(@"S1_2" "SYS_ALIAS_1"@"S1_2" ("EMP"."MGR" "EMP"."EMPNO"))
                    PUSH_SUBQ(@"S2")
                    FULL(@"S2" "E2"@"S2")
                    END_OUTLINE_DATA
                */
              
              Predicate Information (identified by operation id):
              ---------------------------------------------------
              
                 2 - access("E"."MGR" IS NULL)
                 3 - filter((LNNVL("E"."MGR" IS NULL) AND  IS NULL))
                 4 - filter(("E2"."SAL">800 AND LNNVL("E2"."EMPNO"<>"FIRE"(:B1))))
              
              
              58 rows selected.
              

              комментарий от Саян Малакшинов — 09.09.2012 @ 02:06 | Ответить

              • Окончательное подтверждение работы фильтра из 10200:

                -- первая часть - вторая строка плана: |*  2 |   INDEX RANGE SCAN  | IDX_MGR2 2 - access("E"."MGR" IS NULL) :
                ktrgtc2(): started for block <0x0004 : 0x01000233> objd: 0x0001ac5b -- obj_id=109659 => obj_name = IDX_MGR2
                  env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 97sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexc(): returning 8 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgtc2(): completed for block <0x0004 : 0x01000233> objd: 0x0001ac5b  -- obj_id=109659 => obj_name = IDX_MGR2
                FETCH #3:c=0,e=8129,p=0,cr=1,cu=0,mis=0,r=1,dep=0,og=1,plh=3021982181,tim=54162663311
                WAIT #3: nam='SQL*Net message from client' ela= 240 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=54162663607
                -- 3 и 4 строки плана: 
                ktrgtc2(): started for block <0x0004 : 0x01000233> objd: 0x0001ac5b -- obj_id=109659 => obj_name = IDX_MGR2
                  env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 97sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexc(): returning 8 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgtc2(): completed for block <0x0004 : 0x01000233> objd: 0x0001ac5b -- obj_id=109659 => obj_name = IDX_MGR2
                
                -- 1
                WAIT #3: nam='Disk file operations I/O' ela= 320 FileOperation=2 fileno=4 filetype=2 obj#=-1 tim=54162664106
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 97sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 2
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 3
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 4
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 5
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 6
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 7
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 8
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 9
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 10
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 11
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 12
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                -- 13
                ktrget2(): started for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                env: (scn: 0x0000.0348239f  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  statement num=0  parent xid: xid: 0x0000.000.00000000  scn: 0x0000.00000000 96sch: scn: 0x0000.00000000  mascn: (scn: 0x0000.03482364)
                ktrexf(): returning 9 on:  30A472D4  scn: 0xffff.ffffffff  xid: 0x0000.000.00000000  uba: 0x00000000.0000.00  scn: 0xffff.ffffffff  sfl: 0
                ktrgcm(): completed for block  <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                ktrget2(): completed for  block <0x0004 : 0x0100023b> objd: 0x0001ac5c -- obj_id=109660 => obj_name = EMP2
                

                комментарий от Саян Малакшинов — 09.09.2012 @ 03:04 | Ответить

                • Блин, меня клинануло. Все же просто — надо глядеть в starts. Видно же что по подзапросу идет 13 starts — сразу ясно что фильтр выполнился для каждой строки фулскана кроме той что отсеклась по lnnvl

                  комментарий от Саян Малакшинов — 09.09.2012 @ 03:21 | Ответить

                  • А cr =1 потому что один блок прочитало при фулскане

                    комментарий от Саян Малакшинов — 09.09.2012 @ 03:23 | Ответить

                  • всё так, порядок доступа не изменился, несмотря на PUSH_SUBQ(@»S2″):

                    1) INDEX FULL SCAN IDX_MGR2
                    2) для каждой из 13-то строк, подходящих по условию LNNVL(«E».»MGR» IS NULL) выполняется FTS EMP2, возвращающий по 12 строк по условию filter((«E2″.»SAL»>800 AND LNNVL(«E2».»EMPNO»»FIRE»(:B1))))

                    получаемый result set: 13 Starts по 12 A-rows

                    по статистике:

                    table scan rows gotten 182

                    — обработано при этом 13*14 строк EMP2

                    комментарий от Igor Usoltsev — 09.09.2012 @ 10:39 | Ответить


RSS feed for comments on this post. TrackBack URI

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

Создайте бесплатный сайт или блог на WordPress.com.

%d такие блоггеры, как: