Про недокументированные хинты, partitions & collections cardinality.

dbms_photoshop
Дата: 17.12.2012 20:39:11
Привет.
В теме фигурирует много всего, но оно все взаимосвязано, может кому-то будет полезно. Вопрос всего один и ближе к концу.
+ Есть такая таблица
drop table fact;

create table fact
(
  dt                 date                       not null,
  created            timestamp(6)               not null,
  expired            timestamp(6)               not null,
  value              number
)
partition by range (dt)
interval (numtodsinterval(1,'DAY'))
(partition empty values less than (date '2001-01-01'));
    
insert into fact
select t.*, dbms_random.value
from
(select trunc(sysdate)-level, systimestamp, date '9999-01-01' from dual connect by level <= 1000) t,
(select dummy from dual connect by level <= 1);

exec dbms_stats.gather_table_stats (user,'t_fact');
Необходимо сделать слебующее обновление:
update fact
   set expired = created
 where dt in
(select
        to_date (substr(column_value,1,8), 'yyyymmdd')
   from table (sys.odcivarchar2list ('20120101')));
В данном случае все ок.
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                         |      |     1 |    37 |  3467   (1)| 00:00:42 |       |       |
|   1 |  UPDATE                                  | FACT |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                           |      |     1 |    37 |  3467   (1)| 00:00:42 |       |       |
|   3 |    SORT UNIQUE                           |      |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
|   4 |     COLLECTION ITERATOR CONSTRUCTOR FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
|   5 |    PARTITION RANGE ITERATOR              |      |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
|*  6 |     TABLE ACCESS FULL                    | FACT |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
-----------------------------------------------------------------------------------------------------------------
Теперь задача усложняется и надо обновлять не всю секцию целиком, а строки, созданные в определенное время.
update fact
   set expired = created
 where (dt, created) in
(select
        to_date (substr(column_value,1,8), 'yyyymmdd'),
--        to_timestamp(substr(column_value,instr(column_value,' ')),'yyyymmddhh24missff6')
        to_timestamp(substr(column_value,10),'yyyymmddhh24missff6')                       
   from table(sys.odcivarchar2list('20120101 20120101202020000000')) t);
План уже не такой как хотелось бы:
----------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                        |      |     1 |    37 | 13523   (1)| 00:02:43 |       |       |
|   1 |  UPDATE                                 | FACT |       |       |            |          |       |       |
|*  2 |   HASH JOIN SEMI                        |      |     1 |    37 | 13523   (1)| 00:02:43 |       |       |
|   3 |    PARTITION RANGE ALL                  |      |     1 |    35 | 13493   (1)| 00:02:42 |     1 |1048575|
|   4 |     TABLE ACCESS FULL                   | FACT |     1 |    35 | 13493   (1)| 00:02:42 |     1 |1048575|
|   5 |    COLLECTION ITERATOR CONSTRUCTOR FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
----------------------------------------------------------------------------------------------------------------
Это исправляется следующим образом:
update /*+ use_nl(fact) leading(kokbf$0 fact) */ fact
   set expired = created
 where (dt, created) in
(select
        to_date (substr(column_value,1,8), 'yyyymmdd'),
--        to_timestamp(substr(column_value,instr(column_value,' ')),'yyyymmddhh24missff6')
        to_timestamp(substr(column_value,10),'yyyymmddhh24missff6')                       
   from table(sys.odcivarchar2list('20120101 20120101202020000000')) t);
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                         |      |     1 |    37 | 55074   (1)| 00:11:01 |       |       |
|   1 |  UPDATE                                  | FACT |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                           |      |     1 |    37 | 55074   (1)| 00:11:01 |       |       |
|   3 |    SORT UNIQUE                           |      |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
|   4 |     COLLECTION ITERATOR CONSTRUCTOR FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
|   5 |    PARTITION RANGE ITERATOR              |      |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
|*  6 |     TABLE ACCESS FULL                    | FACT |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
-----------------------------------------------------------------------------------------------------------------
Здесь можно обратить внимание на стоимость которая увеличилась более чем в 15 раз из-за чего Оракл и стал предпочитать PARTITION RANGE ALL без хинта.
+ Про хинт cardinality
Интересный эффект наблюдается в зависимости от того каким образом получать timestamp из строки.
Таким образом хинт не подхватывается, но при указании алиаса все ок:
update fact
   set expired = created
 where (dt, created) in
(select --+ cardinality(10)
        to_date (substr(column_value,1,8), 'yyyymmdd'),
--        to_timestamp(substr(column_value,instr(column_value,' ')),'yyyymmddhh24missff6')
        to_timestamp(substr(column_value,10),'yyyymmddhh24missff6')                       
   from table(sys.odcivarchar2list('20120101 20120101202020000000')) t);
----------------------------------------------------------------------------------------------------------------
| Id  | Operation                               | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                        |      |     1 |    37 | 13523   (1)| 00:02:43 |       |       |
|   1 |  UPDATE                                 | FACT |       |       |            |          |       |       |
|*  2 |   HASH JOIN SEMI                        |      |     1 |    37 | 13523   (1)| 00:02:43 |       |       |
|   3 |    PARTITION RANGE ALL                  |      |     1 |    35 | 13493   (1)| 00:02:42 |     1 |1048575|
|   4 |     TABLE ACCESS FULL                   | FACT |     1 |    35 | 13493   (1)| 00:02:42 |     1 |1048575|
|   5 |    COLLECTION ITERATOR CONSTRUCTOR FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
----------------------------------------------------------------------------------------------------------------
update fact
   set expired = created
 where (dt, created) in
(select --+ cardinality(t 10)
        to_date (substr(column_value,1,8), 'yyyymmdd'),
--        to_timestamp(substr(column_value,instr(column_value,' ')),'yyyymmddhh24missff6')
        to_timestamp(substr(column_value,10),'yyyymmddhh24missff6')                       
   from table(sys.odcivarchar2list('20120101 20120101202020000000')) t);
-----------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
-----------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                         |      |     1 |    37 |    97   (2)| 00:00:02 |       |       |
|   1 |  UPDATE                                  | FACT |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                           |      |     1 |    37 |    97   (2)| 00:00:02 |       |       |
|   3 |    SORT UNIQUE                           |      |    10 |    20 |    29   (0)| 00:00:01 |       |       |
|   4 |     COLLECTION ITERATOR CONSTRUCTOR FETCH|      |    10 |    20 |    29   (0)| 00:00:01 |       |       |
|   5 |    PARTITION RANGE ITERATOR              |      |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
|*  6 |     TABLE ACCESS FULL                    | FACT |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
-----------------------------------------------------------------------------------------------------------------
Если получать timestamp несколько иначе, то создается non-mergeable view (мне до конца неясно по каким причинам) и хинт подхватывается без алиаса (правда для этапа SORT UNIQUE, а не COLLECTION ITERATOR CONSTRUCTOR FETCH):
update fact
   set expired = created
 where (dt, created) in
(select --+ cardinality(10)
        to_date (substr(column_value,1,8), 'yyyymmdd'),
        to_timestamp(substr(column_value,instr(column_value,' ')),'yyyymmddhh24missff6')
--        to_timestamp(substr(column_value,10),'yyyymmddhh24missff6')                       
   from table(sys.odcivarchar2list('20120101 20120101202020000000')) t);
----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                 | Name     | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                          |          |     1 |    53 |   165   (1)| 00:00:02 |       |       |
|   1 |  UPDATE                                   | FACT     |       |       |            |          |       |       |
|   2 |   NESTED LOOPS                            |          |     1 |    53 |   165   (1)| 00:00:02 |       |       |
|   3 |    VIEW                                   | VW_NSO_1 |    10 |   180 |    30   (4)| 00:00:01 |       |       |
|   4 |     SORT UNIQUE                           |          |    10 |    20 |    30   (4)| 00:00:01 |       |       |
|   5 |      COLLECTION ITERATOR CONSTRUCTOR FETCH|          |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
|   6 |    PARTITION RANGE ITERATOR               |          |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
|*  7 |     TABLE ACCESS FULL                     | FACT     |     1 |    35 |    13   (0)| 00:00:01 |   KEY |   KEY |
----------------------------------------------------------------------------------------------------------------------
Без хинта план выглядит так:
update fact
   set expired = created
 where (dt, created) in
(select
        to_date (substr(column_value,1,8), 'yyyymmdd'),
        to_timestamp(substr(column_value,instr(column_value,' ')),'yyyymmddhh24missff6')
--        to_timestamp(substr(column_value,10),'yyyymmddhh24missff6')                       
   from table(sys.odcivarchar2list('20120101 20120101202020000000')) t);
----------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                 | Name     | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
----------------------------------------------------------------------------------------------------------------------
|   0 | UPDATE STATEMENT                          |          |     1 |    53 | 13524   (1)| 00:02:43 |       |       |
|   1 |  UPDATE                                   | FACT     |       |       |            |          |       |       |
|*  2 |   HASH JOIN                               |          |     1 |    53 | 13524   (1)| 00:02:43 |       |       |
|   3 |    PARTITION RANGE ALL                    |          |     1 |    35 | 13493   (1)| 00:02:42 |     1 |1048575|
|   4 |     TABLE ACCESS FULL                     | FACT     |     1 |    35 | 13493   (1)| 00:02:42 |     1 |1048575|
|   5 |    VIEW                                   | VW_NSO_1 |  8168 |   143K|    30   (4)| 00:00:01 |       |       |
|   6 |     SORT UNIQUE                           |          |  8168 | 16336 |    30   (4)| 00:00:01 |       |       |
|   7 |      COLLECTION ITERATOR CONSTRUCTOR FETCH|          |  8168 | 16336 |    29   (0)| 00:00:01 |       |       |
----------------------------------------------------------------------------------------------------------------------
Собственно главный вопрос в следующем.
На одном из экземпляров, кардинальность по умолчанию была 1 а не 8168, поэтому никаких проблем не возникало.
На всех экземплярах optimizer_dynamic_sampling = 2, associate statistics не используется. По какой причине еще по умолчанию может быть 1?
+ Альтернативы
Проблем с кардинальностью гарантированно можно избежать при merge (PARTITION RANGE SUBQUERY) или forall (несколько PARTITION RANGE SINGLE), но интересует именно update.
Серафимный Шестикрыл
Дата: 17.12.2012 21:41:00
dbms_photoshop,

версия какая ?
_Nikotin
Дата: 17.12.2012 21:53:01
outline/profile/baseline ?
Серафимный Шестикрыл
Дата: 17.12.2012 21:59:52
_Nikotin
outline/profile/baseline ?


Cardinality feedback ? :)

SQL> select * from table(p(1,2,3));

COLUMN_VALUE                                                                                                                                                                                            
------------                                                                                                                                                                                            
           1                                                                                                                                                                                            
           2                                                                                                                                                                                            
           3                                                                                                                                                                                            

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  bjf61wwxfu8dk, child number 0                                                                                                                                                                   
-------------------------------------                                                                                                                                                                   
select * from table(p(1,2,3))                                                                                                                                                                           
                                                                                                                                                                                                        
Plan hash value: 1748000095                                                                                                                                                                             
                                                                                                                                                                                                        
----------------------------------------------------------------------------------------------                                                                                                          
| Id  | Operation                             | Name | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                          
----------------------------------------------------------------------------------------------                                                                                                          
|   0 | SELECT STATEMENT                      |      |       |       |    29 (100)|          |                                                                                                          
|   1 |  COLLECTION ITERATOR CONSTRUCTOR FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |                                                                                                          

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------                                                                                                          
                                                                                                                                                                                                        

13 rows selected.

SQL> select * from table(p(1,2,3));

COLUMN_VALUE                                                                                                                                                                                            
------------                                                                                                                                                                                            
           1                                                                                                                                                                                            
           2                                                                                                                                                                                            
           3                                                                                                                                                                                            

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  bjf61wwxfu8dk, child number 1                                                                                                                                                                   
-------------------------------------                                                                                                                                                                   
select * from table(p(1,2,3))                                                                                                                                                                           
                                                                                                                                                                                                        
Plan hash value: 1748000095                                                                                                                                                                             
                                                                                                                                                                                                        
----------------------------------------------------------------------------------------------                                                                                                          
| Id  | Operation                             | Name | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                          
----------------------------------------------------------------------------------------------                                                                                                          
|   0 | SELECT STATEMENT                      |      |       |       |    29 (100)|          |                                                                                                          
|   1 |  COLLECTION ITERATOR CONSTRUCTOR FETCH|      |     3 |     6 |    29   (0)| 00:00:01 |                                                                                                          

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------                                                                                                          
                                                                                                                                                                                                        
Note                                                                                                                                                                                                    
-----                                                                                                                                                                                                   
   - cardinality feedback used for this statement                                                                                                                                                       
                                                                                                                                                                                                        

17 rows selected.

SQL> alter session set "_optimizer_use_feedback" = false;

Session altered.

SQL> select * from table(p(1,2,3,4));

COLUMN_VALUE                                                                                                                                                                                            
------------                                                                                                                                                                                            
           1                                                                                                                                                                                            
           2                                                                                                                                                                                            
           3                                                                                                                                                                                            
           4                                                                                                                                                                                            

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  2hrzk2z924b1v, child number 0                                                                                                                                                                   
-------------------------------------                                                                                                                                                                   
select * from table(p(1,2,3,4))                                                                                                                                                                         
                                                                                                                                                                                                        
Plan hash value: 1748000095                                                                                                                                                                             
                                                                                                                                                                                                        
----------------------------------------------------------------------------------------------                                                                                                          
| Id  | Operation                             | Name | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                          
----------------------------------------------------------------------------------------------                                                                                                          
|   0 | SELECT STATEMENT                      |      |       |       |    29 (100)|          |                                                                                                          
|   1 |  COLLECTION ITERATOR CONSTRUCTOR FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |                                                                                                          

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------                                                                                                          
                                                                                                                                                                                                        

13 rows selected.

SQL> select * from table(p(1,2,3,4));

COLUMN_VALUE                                                                                                                                                                                            
------------                                                                                                                                                                                            
           1                                                                                                                                                                                            
           2                                                                                                                                                                                            
           3                                                                                                                                                                                            
           4                                                                                                                                                                                            

SQL> select * from table(dbms_xplan.display_cursor);

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID  2hrzk2z924b1v, child number 0                                                                                                                                                                   
-------------------------------------                                                                                                                                                                   
select * from table(p(1,2,3,4))                                                                                                                                                                         
                                                                                                                                                                                                        
Plan hash value: 1748000095                                                                                                                                                                             
                                                                                                                                                                                                        
----------------------------------------------------------------------------------------------                                                                                                          
| Id  | Operation                             | Name | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                          
----------------------------------------------------------------------------------------------                                                                                                          
|   0 | SELECT STATEMENT                      |      |       |       |    29 (100)|          |                                                                                                          
|   1 |  COLLECTION ITERATOR CONSTRUCTOR FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |                                                                                                          

PLAN_TABLE_OUTPUT                                                                                                                                                                                       
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------
dbms_photoshop
Дата: 17.12.2012 22:07:37
_Nikotin
outline/profile/baseline ?
Нет.
Серафимный Шестикрыл
Cardinality feedback ?
Да, походу это оно, спасибо!
В оригинале используются на самом деле коллекции как бинды, я просто использовал конструкторы для демонстрации.

З.Ы. Какое у тебя разрешение монитора, что такие простыни влезают? :)
Серафимный Шестикрыл
Дата: 17.12.2012 22:09:28
dbms_photoshop
З.Ы. Какое у тебя разрешение монитора, что такие простыни влезают? :)


есть такое специальное - spool называется ;)
_Nikotin
Дата: 18.12.2012 11:25:52
Серафимный Шестикрыл
_Nikotin
outline/profile/baseline ?


Cardinality feedback ? :)

Cardinality feedback реализован через baseline, так что :-)
Серафимный Шестикрыл
Дата: 18.12.2012 12:38:31
_Nikotin
Cardinality feedback реализован через baseline, так что :-)


Интересно было бы глянуть ?
_Nikotin
Дата: 18.12.2012 16:57:33
Серафимный Шестикрыл,

Хм, еле откопал comment-33909
Jonathan Lewis
January 8, 2011 at 5:16 am
... “cardinality feedback” happens automatically, and works by creating SQL Baselines ...

Сам не проверял. Встречал много раз как люди пишут большие статьи в блоги про как же так может быть:
автор
Note
-----
- SQL plan baseline SQL_PLAN_xxxxxxxxxxxxxxxxxxx used for this statement
- cardinality feedback used for this statement
Серафимный Шестикрыл
Дата: 18.12.2012 17:19:00
_Nikotin
Jonathan Lewis
January 8, 2011 at 5:16 am
... “cardinality feedback” happens automatically, and works by creating SQL Baselines ...



Jonathan Lewis
A further “corroborative” thought –...