Friday, February 12, 2016

Session Statistics on Global Temporary Tables in Oracle 12c

Prior to Oracle 12c the database don’t maintain separate statistics for global temporary table (GTT), the database maintains one version of statistics shared by all session, even though the data across sessions  could differ.

Starting with 12c we can set the table-level preference GLOBAL_TEMP_TABLE_STATS to either shared or session-specific (global preference default GLOBAL_TEMP_TABLE_STATS to SESSION). Users can gather statistics on GTT and can have own version of session statistics. During optimization the optimizer first check if session statistics exists if yes, then make use of them. If not optimizer uses Shared statistics if they exist.

rajesh@ORA12C> select dbms_stats.get_prefs('GLOBAL_TEMP_TABLE_STATS')
  2  from dual ;

DBMS_STATS.GET_PREFS('GLOBAL_TEMP_TABLE_STATS')
------------------------------------------------------------------------
SESSION

1 row selected.

rajesh@ORA12C> create global temporary table t(x int);

Table created.

rajesh@ORA12C> insert into t(x) values(0);

1 row created.

rajesh@ORA12C> select num_rows,blocks,last_analyzed,scope
  2  from user_tab_statistics
  3  where table_name ='T';

  NUM_ROWS     BLOCKS LAST_ANALYZ SCOPE
---------- ---------- ----------- -------
                                  SHARED

1 row selected.

rajesh@ORA12C> exec dbms_stats.gather_table_stats(user,'T');

PL/SQL procedure successfully completed.

rajesh@ORA12C> select num_rows,blocks,last_analyzed,scope
  2  from user_tab_statistics
  3  where table_name ='T';

  NUM_ROWS     BLOCKS LAST_ANALYZ SCOPE
---------- ---------- ----------- -------
                                  SHARED
         1          1 11-FEB-2016 SESSION

2 rows selected.

We have session level statistics in place, the optimizer now uses this session statistics for optimal execution.

rajesh@ORA12C> select count(data_object_id)
  2  from big_table
  3  where id in (select x from t) ;

COUNT(DATA_OBJECT_ID)
---------------------
                    0

1 row selected.

rajesh@ORA12C> @xplan_cursor

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  3zwrtp37uvz2b, child number 0
-------------------------------------
select count(data_object_id) from big_table where id in (select x from
t)

Plan hash value: 3252046534

---------------------------------------------------------------------------------
| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |            |       |       |     5 (100)|
|   1 |  SORT AGGREGATE               |            |     1 |     9 |            |
|   2 |   NESTED LOOPS                |            |     1 |     9 |     5  (20)|
|   3 |    NESTED LOOPS               |            |     1 |     9 |     5  (20)|
|   4 |     SORT UNIQUE               |            |     1 |     2 |     2   (0)|
|   5 |      TABLE ACCESS FULL        | T          |     1 |     2 |     2   (0)|
|*  6 |     INDEX RANGE SCAN          | BIG_IDX_01 |     1 |       |     2   (0)|
|   7 |    TABLE ACCESS BY INDEX ROWID| BIG_TABLE  |     1 |     7 |     2   (0)|
---------------------------------------------------------------------------------

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

   6 - access("ID"="X")

Note
-----
   - this is an adaptive plan
   - Global temporary table session private statistics used


30 rows selected.

The notes section from the plan, shows that it used session statistics for optimization.  When loaded with few more data and did stats gathering, the existing cursor for this session from shared pool got re-loaded with new set of session statistics.

rajesh@ORA12C>
rajesh@ORA12C> insert into t(x)
  2  select rownum
  3  from all_objects
  4  where rownum <=1000;

1000 rows created.

rajesh@ORA12C> exec dbms_stats.gather_table_stats(user,'T');

PL/SQL procedure successfully completed.

rajesh@ORA12C> select num_rows,blocks,last_analyzed,scope
  2  from user_tab_statistics
  3  where table_name ='T';

  NUM_ROWS     BLOCKS LAST_ANAL SCOPE
---------- ---------- --------- -------
                                SHARED
      1001          2 11-feb-16 SESSION

2 rows selected.

rajesh@ORA12C> set autotrace traceonly explain
rajesh@ORA12C> select *
  2  from big_table
  3  where id in (select x from t) ;

Execution Plan
----------------------------------------------------------
Plan hash value: 2358921057

--------------------------------------------------------------------------------
| Id  | Operation                    | Name       | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |            |  1001 |   108K|  1506   (1)|
|   1 |  NESTED LOOPS                |            |  1001 |   108K|  1506   (1)|
|   2 |   NESTED LOOPS               |            |  1001 |   108K|  1506   (1)|
|   3 |    SORT UNIQUE               |            |  1001 |  4004 |     2   (0)|
|   4 |     TABLE ACCESS FULL        | T          |  1001 |  4004 |     2   (0)|
|*  5 |    INDEX RANGE SCAN          | BIG_IDX_01 |     1 |       |     2   (0)|
|   6 |   TABLE ACCESS BY INDEX ROWID| BIG_TABLE  |     1 |   107 |     3   (0)|
--------------------------------------------------------------------------------

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

   5 - access("ID"="X")

Note
-----
   - this is an adaptive plan
   - Global temporary table session private statistics used

rajesh@ORA12C> set autotrace off
rajesh@ORA12C>

Now from a different session tried this.

rajesh@ORA12C> select num_rows,blocks,last_analyzed,scope
  2  from user_tab_statistics
  3  where table_name ='T';

  NUM_ROWS     BLOCKS LAST_ANALYZ SCOPE
---------- ---------- ----------- -------
                                  SHARED

1 row selected.

rajesh@ORA12C> set serveroutput off
rajesh@ORA12C> select count(data_object_id)
  2  from big_table
  3  where id in (select x from t) ;

COUNT(DATA_OBJECT_ID)
---------------------
                    0

1 row selected.

rajesh@ORA12C> @xplan_cursor

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------
SQL_ID  3zwrtp37uvz2b, child number 0
-------------------------------------
select count(data_object_id) from big_table where id in (select x from
t)

Plan hash value: 3252046534

---------------------------------------------------------------------------------
| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |            |       |       |     6 (100)|
|   1 |  SORT AGGREGATE               |            |     1 |    20 |            |
|   2 |   NESTED LOOPS                |            |     1 |    20 |     6  (17)|
|   3 |    NESTED LOOPS               |            |     1 |    20 |     6  (17)|
|   4 |     SORT UNIQUE               |            |     1 |    13 |     2   (0)|
|   5 |      TABLE ACCESS FULL        | T          |     1 |    13 |     2   (0)|
|*  6 |     INDEX RANGE SCAN          | BIG_IDX_01 |     1 |       |     2   (0)|
|   7 |    TABLE ACCESS BY INDEX ROWID| BIG_TABLE  |     1 |     7 |     3   (0)|
---------------------------------------------------------------------------------

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

   6 - access("ID"="X")

Note
-----
   - dynamic statistics used: dynamic sampling (level=2)
   - this is an adaptive plan


30 rows selected.

Since no session-specific and shared statistics Dynamic sampling kicked in, with shared statistics in place, optimizer make use of shared statistics.

rajesh@ORA12C>
rajesh@ORA12C>
rajesh@ORA12C> begin
  2     dbms_stats.set_table_prefs(user,'T',
  3             'GLOBAL_TEMP_TABLE_STATS','SHARED');
  4     dbms_stats.set_table_stats(user,'T',
  5             numrows=>5000,
  6             numblks=>500);
  7  end;
  8  /

PL/SQL procedure successfully completed.

rajesh@ORA12C> select num_rows,blocks,last_analyzed,scope
  2  from user_tab_statistics
  3  where table_name ='T';

  NUM_ROWS     BLOCKS LAST_ANALYZ SCOPE
---------- ---------- ----------- -------
      5000        500 11-FEB-2016 SHARED

1 row selected.

rajesh@ORA12C> begin
  2     dbms_stats.set_table_prefs(user,'T',
  3             'GLOBAL_TEMP_TABLE_STATS','SESSION');
  4  end;
  5  /

PL/SQL procedure successfully completed.

rajesh@ORA12C> select num_rows,blocks,last_analyzed,scope
  2  from user_tab_statistics
  3  where table_name ='T';

  NUM_ROWS     BLOCKS LAST_ANALYZ SCOPE
---------- ---------- ----------- -------
      5000        500 11-FEB-2016 SHARED

1 row selected.

rajesh@ORA12C> select count(data_object_id)
  2  from big_table
  3  where id in (select x from t) ;

COUNT(DATA_OBJECT_ID)
---------------------
                    0

1 row selected.

rajesh@ORA12C>
rajesh@ORA12C> @xplan_cursor

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------
SQL_ID  3zwrtp37uvz2b, child number 0
-------------------------------------
select count(data_object_id) from big_table where id in (select x from
t)

Plan hash value: 3252046534

---------------------------------------------------------------------------------
| Id  | Operation                     | Name       | Rows  | Bytes | Cost (%CPU)|
---------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |            |       |       |   606 (100)|
|   1 |  SORT AGGREGATE               |            |     1 |    20 |            |
|   2 |   NESTED LOOPS                |            |   156 |  3120 |   606   (1)|
|   3 |    NESTED LOOPS               |            |   156 |  3120 |   606   (1)|
|   4 |     SORT UNIQUE               |            |  5000 | 65000 |   137   (0)|
|   5 |      TABLE ACCESS FULL        | T          |  5000 | 65000 |   137   (0)|
|*  6 |     INDEX RANGE SCAN          | BIG_IDX_01 |     1 |       |     2   (0)|
|   7 |    TABLE ACCESS BY INDEX ROWID| BIG_TABLE  |     1 |     7 |     3   (0)|
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   6 - access("ID"="X")

Note
-----
   - this is an adaptive plan


29 rows selected.

See, no indication of “session private statistics” in notes section.  With Session-specific and shared statistics in place, session statistics take more preference for optimization.

rajesh@ORA12C> insert into t(x)
  2  select rownum
  3  from all_objects;

89984 rows created.

rajesh@ORA12C>
rajesh@ORA12C> exec dbms_stats.gather_table_stats(user,'T');

PL/SQL procedure successfully completed.

rajesh@ORA12C> select num_rows,blocks,last_analyzed,scope
  2  from user_tab_statistics
  3  where table_name ='T';

  NUM_ROWS     BLOCKS LAST_ANALYZ SCOPE
---------- ---------- ----------- -------
      5000        500 11-FEB-2016 SHARED
     89984        137 11-FEB-2016 SESSION

2 rows selected.

rajesh@ORA12C> select count(data_object_id)
  2  from big_table
  3  where id in (select x from t) ;

COUNT(DATA_OBJECT_ID)
---------------------
                 6534

1 row selected.

rajesh@ORA12C>
rajesh@ORA12C> @xplan_cursor

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  3zwrtp37uvz2b, child number 0
-------------------------------------
select count(data_object_id) from big_table where id in (select x from
t)

Plan hash value: 2375446597

-----------------------------------------------------------------------
| Id  | Operation            | Name      | Rows  | Bytes | Cost (%CPU)|
-----------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |       |       |  4675 (100)|
|   1 |  SORT AGGREGATE      |           |     1 |    12 |            |
|*  2 |   HASH JOIN          |           | 89984 |  1054K|  4675   (1)|
|   3 |    SORT UNIQUE       |           | 89984 |   439K|    39   (0)|
|   4 |     TABLE ACCESS FULL| T         | 89984 |   439K|    39   (0)|
|   5 |    TABLE ACCESS FULL | BIG_TABLE |  1000K|  6835K|  4355   (1)|
-----------------------------------------------------------------------

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

   2 - access("ID"="X")

Note
-----
   - this is an adaptive plan
   - Global temporary table session private statistics used


28 rows selected.

rajesh@ORA12C>

1 comment: