IN ORACLE MILIEU …

Beyond Knowledge

SGA Re-Sizes Occurring Despite AMM/ASMM Being Disabled – Oracle 11.2

Its been observed that after upgrading the database to 11gR2 (11.2.0.2 onwards) SGA components are getting resized even when automatic memory management (MEMORY_TARGET=0) and automatic shared memory management (SGA_TARGET=0) is disabled. Resize are happening to an extent that the component sizes are shrinking below the minimum size set by DBAs.

This typically appears as growth in the __SHARED_POOL_SIZE value and a reduction in the __DB_CACHE_SIZE value being used in the instance, such that __DB_CACHE_SIZE may be shrunk below the DB_CACHE_SIZE value specified in the init.ora/spfile.

(In case if you are not aware __<param name> are the dynamic value of the component after database start up)

Checking V$MEMORY_RESIZE_OPS / V$SGA_RESIZE_OPS shows an IMMEDIATE re-size having occurred

SQL>select COMPONENT, PARAMETER, INITIAL_SIZE/1024/1024 "INITIAL_SIZE",FINAL_SIZE/1024/1024 "FINAL_SIZE", STATUS from v$SGA_RESIZE_OPS where component in ('DEFAULT buffer cache','shared pool') order by component, start_time;

COMPONENT PARAMETER INITIAL_SIZE FINAL_SIZE STATUS
---------------------------------------- ------------------------------ ------------ ---------- ---------
DEFAULT buffer cache db_cache_size 3280 3264 COMPLETE
DEFAULT buffer cache db_cache_size 3280 3264 COMPLETE
DEFAULT buffer cache db_cache_size 0 3280 COMPLETE
DEFAULT buffer cache db_cache_size 3264 3248 COMPLETE
DEFAULT buffer cache db_cache_size 3248 3232 COMPLETE
DEFAULT buffer cache db_cache_size 3232 3216 COMPLETE
DEFAULT buffer cache db_cache_size 3216 3200 COMPLETE
DEFAULT buffer cache db_cache_size 3200 3184 COMPLETE
DEFAULT buffer cache db_cache_size 3184 3168 COMPLETE
shared pool shared_pool_size 0 1024 COMPLETE
shared pool shared_pool_size 1024 1040 COMPLETE

COMPONENT PARAMETER INITIAL_SIZE FINAL_SIZE STATUS
---------------------------------------- ------------------------------ ------------ ---------- ---------
shared pool shared_pool_size 1040 1056 COMPLETE
shared pool shared_pool_size 1056 1072 COMPLETE
shared pool shared_pool_size 1072 1088 COMPLETE
shared pool shared_pool_size 1088 1104 COMPLETE
shared pool shared_pool_size 1104 1120 COMPLETE
shared pool shared_pool_size 1120 1136 COMPLETE

17 rows selected.

SQL>

You might think this is a bug, but this is an expected behavior. This is done to prevent an ORA-4031 error from being raised. So if shared_pool falls short of memory, Oracle extracts memory from db_buffer_cache and allocates the same to shared_pool.

But, enhancement Bug:13340694 has been logged to provide a warning message in the alertlog when such allocations occur to provide the DBA with an indication of a memory problem, as otherwise this can result in the buffer cache being shrunk below the specified minimum value, and hence lead to performance degradation

Is it possible to disable this expected behavior ?

Yes, this feature can be disabled by setting _MEMORY_IMM_MODE_WITHOUT_AUTOSGA=false. This can be done using alter system command.


connect / as sysdba

alter system set "_memory_imm_mode_without_autosga"=FALSE scope=both;

exit

References:

1269139.1 - https://support.oracle.com/CSP/main/article?cmd=show&type=NOT&doctype=BULLETIN&id=1269139.1

Hope this helps !!

Removing targets from Grid Control – Oracle 11g

Many times we have to cleanup some old targets from grid control. This happens because of host getting deprecated.

Example if you have database which was running on a host and is registered in grid. Because of scaling requirement you moved the database to bigger box. In that case you have to have agent installation done on new box and you have to remove old target from the grid.

Not doing this will make your grid stale with old data.

We can clean up grid from the Grid UI. Only problem with this approach is that this is time consuming.

When we have a database running on a host, its not just 1 target that agent discovers. We have multiple targets like host, database listeners etc. Deleting each target takes time.

Easiest approach for deleting the target is from backend database. We have grid database which stores complete grid information.

We can follow below steps to remove target from backend without using grid UI

1) Stop the management agent on the target database

% <AGENT_HOME>/bin/emctl stop agent

2) Log onto grid database (also called repository database) and search for target

SQL> select target_name from mgmt_targets where target_type = ‘oracle_emd’ and target_name like ‘<search_string>’;

This shows registered agents as per search string

3) Once you identify the target name, you below command to delete the target

exec mgmt_admin.cleanup_agent(‘<target_name>’);

Check the grid UI now for this target and you will not find it.

Hope this helps !!

Inside Index block – Oracle Database 11g

I ran into a scenario where I really wanted to check whats inside the leaf block of an index.

This post is to dig inside the index leaf block and find out various facts stored inside a block.

We will start with taking a dump of a block

SYS.ORCL>alter session set tracefile_identifier='INDEX_BLOCK_DUMP';
Session altered.
SYS.ORCL>alter system dump datafile 292 block 245790;
System altered.
SYS.ORCL>

You will get a trace file having name something like “<DB_NAME>_ora_<PID>_INDEX_BLOCK_DUMP.trc” in your trace directory

Even if you dont use tracefile_identifier, you can still see the name of the dump file using following query

SYS.ORCL>select value from v$diag_info where name = 'Default Trace File';
VALUE
-----------------------------------------------------------------
/dumps-01/diag/rdbms/orcl_b/orcl/trace/orcl_ora_11416.trc
1 row selected.
SYS.ORCL>

At the start of the block you will see following details

*********************************************************************
Start dump data blocks tsn: 17 file#:292 minblk 245790 maxblk 245790
Block dump from cache:
Dump of buffer cache at level 4 for tsn=17, rdba=1224982558
Block dump from disk:
buffer tsn: 17 rdba: 0x4903c01e (292/245790)
scn: 0x06ca.4d4684c7 seq: 0x01 flg: 0x04 tail: 0x84c70601
frmt: 0x02 chkval: 0xe178 type: 0x06=trans data
*********************************************************************

tsn –> Tablespace Number = 17
file# –> File # = 292
minblk & maxblk –> Min block and Max block numbers = 245790
rdba –> Relative data block address. Its value is 0x4903c01e in hexa decimal and 1224982558 in decimal.
We can get rdba using dbms_utility.make_data_block_address

SYS.ORCL>select dbms_utility.make_data_block_address(292,245790) from dual;
DBMS_UTILITY.MAKE_DATA_BLOCK_ADDRESS(292,245790)
------------------------------------------------
 1224982558

After above lines we have dump of memory. You will see a line something as below

Dump of memory from 0x00002B5C26C2DA00 to 0x00002B5C26C2FA00

So at each address it will print the content. Its very difficult to read this format.

2B5C26C2DA00 0000A206 4903C01E 4D4684C7 040106CA [.......I..FM....]
2B5C26C2DA10 0000E178 00000002 000551A1 4CFB5368 [x........Q..hS.L]
2B5C26C2DA20 000006CA 0032001D 4903C009 000F0046 [......2....IF...]
2B5C26C2DA30 00221BD6 01460CC8 0001F2FC 06CAC000 [.."...F.........]
2B5C26C2DA40 40FE45EE 001D00EE 0008E5F9 0086EC85 [.E.@............]
2B5C26C2DA50 003EC8BA 06CA8000 40FA9BFF 00000000 [..>........@....]
2B5C26C2DA60 00000000 00000000 00000000 00000000 [................]
2B5C26C2DA70 00000000 00100040 0027C6F8 0085DF28 [....@.....'.(...]

But once this memory dump is complete, we can see some readable information stored in index block. Here is what comes next

seg/obj: 0x551a1 csc: 0x6ca.4cfb5368 itc: 29 flg: E typ: 2 - INDEX
 brn: 0 bdba: 0x4903c009 ver: 0x01 opc: 0
 inc: 0 exflg: 0

seg/obj is the object_id. 0x551a1 = 348577 in 348577 in decimal.

SYS.ORCL>select object_name from dba_objects where data_object_id = 348577;
OBJECT_NAME
-------------------------
UNIQ_FPM_MSG_WHSE_CAT_ID
1 row selected.

This is the index for which we dumped the block.

csc: 0x6ca.4cfb5368 –> This is the comit SCN number of the block
itc: 29 –> Number of interested transaction list(ITL) entry in the block.
typ: 2 – INDEX –> Indicates this is an index block

Next comes the Interested Transaction List(ITL) entries. We had 29 ITL entries as indicated above by itc: 29

Itl  Xid                 Uba                Flag Lck   Scn/Fsc
0x01 0x0046.00f.00221bd6 0x01460cc8.f2fc.01 CB-- 0 scn 0x06ca.40fe45ee
0x02 0x00ee.01d.0008e5f9 0x0086ec85.c8ba.3e C--- 0 scn 0x06ca.40fa9bff
0x03 0x0000.000.00000000 0x00000000.0000.00 ---- 0 fsc 0x0000.00000000
0x04 0x0040.010.0027c6f8 0x0085df28.cb0f.0b C--- 0 scn 0x06ca.40fb14e2
0x05 0x00f0.017.00090d20 0x37860557.c767.36 C--- 0 scn 0x06ca.40fb15a7
0x06 0x013d.020.0002205c 0x3802952d.202e.26 C--- 0 scn 0x06ca.40fd6d83
0x07 0x001d.012.0049340a 0x38063ebe.c0d6.48 C--- 0 scn 0x06ca.40fd6e3e
0x08 0x00b5.018.000e6ee9 0x384279bf.95f1.07 C--- 0 scn 0x06ca.40fe4336
0x09 0x010e.007.000a4514 0x0088535e.b343.19 C--- 0 scn 0x06ca.40fa9b08
0x0a 0x00df.00f.0009db56 0x014571c8.e27e.0a C--- 0 scn 0x06ca.40fe446d
0x0b 0x00f4.00b.00092665 0x37c65073.c520.27 C--- 0 scn 0x06ca.4100f955
0x0c 0x0129.017.00035f0e 0x0187980c.2a1a.04 C--- 0 scn 0x06ca.4100fa77
0x0d 0x007a.002.0010db5b 0x38458cb5.5bbd.20 C--- 0 scn 0x06ca.41168fad
0x0e 0x0069.00e.0017dd7d 0x3807b2e2.1390.30 C--- 0 scn 0x06ca.411691b0
0x0f 0x00d7.006.0009a7ee 0x38417fbc.dd00.19 C--- 0 scn 0x06ca.41169313
0x10 0x0181.003.000170b0 0x00c78696.190d.2b C--- 0 scn 0x06ca.41169bc6
0x11 0x00bd.014.000d2ddf 0x3805ab0d.72f9.19 C--- 0 scn 0x06ca.41169ecc
0x12 0x0001.019.00634f4c 0x3802e302.4fd4.0d C--- 0 scn 0x06ca.4116ab9e
0x13 0x0035.014.003a9a1d 0x014691a5.cda1.3d C--- 0 scn 0x06ca.4116ad76
0x14 0x017a.017.00018a03 0x37c4d213.17c4.0d C--- 0 scn 0x06ca.4116b388
0x15 0x006a.012.0017f16a 0x37878e97.329f.2b C--- 0 scn 0x06ca.4165b46c
0x16 0x00f0.01b.00090d4e 0x3846d24e.c76b.36 C--- 0 scn 0x06ca.4165b57c
0x17 0x0168.004.00019960 0x00c77875.18ab.08 C--- 0 scn 0x06ca.4165b7a5
0x18 0x0104.002.0006b6fd 0x38401fbe.a734.0e C--- 0 scn 0x06ca.4165b951
0x19 0x0044.018.00275fa5 0x38417b7e.cf86.3c C--- 0 scn 0x06ca.41035e03
0x1a 0x0127.01c.0006d44b 0x008602ca.5584.04 C--- 0 scn 0x06ca.40f95f82
0x1b 0x00ba.004.000dbc9a 0x3786425e.74f4.32 C--- 0 scn 0x06ca.40f96071
0x1c 0x0194.004.00014665 0x38464daf.1aca.46 C--- 0 scn 0x06ca.40f9c995
0x1d 0x0014.00c.0066b5d6 0x01865ee3.e323.08 C--- 0 scn 0x06ca.40f9ca31

Itl –> Slot Id of interested transaction
Xid –> Transaction ID od transaction occupying that ITL
Uba –> Undo block address
Flag –> State for current transaction (C – Commited)
Lck –> Number of Lock held by the transacion
Scn/Fsc –> SCN number of the current transaction

Once ITL list is displayed, next comes the leaf block dump.
At the start of leaf block dump it gives the header information of the block

Leaf block dump
===============
header address 47674787290348=0x2b5c26c2dcec     => Header start and end address
kdxcolev 0                                       => Index Level. 0 represent leaf block
KDXCOLEV Flags = - - - 
kdxcolok 0                                       => Indicates that no block transaction happening right now
kdxcoopc 0x80: opcode=0: iot flags=--- is converted=Y    => Internal Operation code
kdxconco 3                                        => Index Column Count
kdxcosdc 8                                        => Count of index structural changes involving block
kdxconro 48                                       => Number of index records in this block
kdxcofbo 132=0x84                                 => Offset from start to the beginning of free space
kdxcofeo 1435=0x59b                               => Offset to end of free space (ie. first portion of block containing index data)
kdxcoavs 1303                                     => Available space in the block (effective space between kdxcofbo and kdxcofeo)
kdxlespl 0                                        => Bytes of uncommited data at the time of block split that have been cleaned out
kdxlende 0                                        => Number of deleted entries
kdxlenxt 830726637=0x3183e1ed                     => Pointer to next leaf block
kdxleprv 830721132=0x3183cc6c                     => Pointer to previous leaf block
kdxledsz 10                                       => Deleted space
kdxlebksz 7384                                    => Size of usable block space

After header, comes the index information as shown below

row#0[3602] flag: ------, lock: 0, len=61, data:(10): 00 0c 13 af 37 47 af ee 00 05
col 0; len 26; (26):
 35 35 39 32 35 38 5f 31 33 35 33 32 35 30 31 34 33 5f 36 38 5e 32 34 31 34
 30
col 1; len 4; (4): 4c 45 4a 31
col 2; len 16; (16): 43 68 61 6e 67 65 50 69 63 6b 53 74 61 74 75 73
row#1[3663] flag: ------, lock: 0, len=57, data:(10): 00 0c 13 af 37 47 af 24 00 42
col 0; len 26; (26):
 35 35 39 22 35 38 5f 31 33 32 33 32 35 30 32 32 33 5f 36 38 5f 32 34 31 34
 30
col 1; len 4; (4): 4c 45 4f 33
col 2; len 12; (12): 50 69 63 6b 43 6f 6d 70 6c 65 74 65

Here is what above values mean

row#0 -> First row in the block. Row starts from 0
[3602] -> Starting location within the block
flag: -> Flag to give information above the record in index leaf block. If you delete a row, the flag will be marked D, meaning deleted.
lock -> Lock information at row level
len -> Total length of a index record
data -> I guess this should be the length of rowid which is following this “00 0c 13 af 37 47 af ee 00 05″

Each row in this index has 3 columns. Columns start with col0. So we have column information in col0, col1 and col2

col0 -> First column
len -> Length of the column. 26 in this case.
Value after that is the actual value in base 16 stored in the block.

We can actually get real value for the values stored above using following function SQL.
You need to pass the hex value seen in block dump to a function dbms_stats.convert_raw_value and it returns the required values.

Lets say we want to find out the value for first row (Value for each column in first row)

SQL>var col1 varchar2(1000);
SQL>exec dbms_stats.convert_raw_value(replace('35 35 39 32 35 38 5f 31 33 32 33 32 35 30 30 32 33 5f 36 38 5f 32 34 31 34 30',' ',''),:col1);
PL/SQL procedure successfully completed.
SQL>var col2 varchar2(10);
SQL>exec dbms_stats.convert_raw_value(replace('4c 45 4a 31',' ',''),:col2);
PL/SQL procedure successfully completed.
SQL>var col3 varchar2(100);
SQL>exec dbms_stats.convert_raw_value(replace('43 68 61 6e 67 65 50 69 63 6b 53 74 61 74 75 73',' ',''),:col3);
PL/SQL procedure successfully completed.
COL1
-----------------------------
559258_1323250023_68_24140

COL2
-----------------------------
ABC

COL3
-----------------------------
EmployeeStatus
SQL>

Similarly you can write your own PLSQL code and find out all the values stored inside an index block.

Hope this helps !!

Oracle SQL Plan Management – Part 3

SQL Plan Management – Oracle Database 11g

You have seen the first 2 parts of SQL plan baselines

Part 1 – http://avdeo.com/2011/06/02/oracle-sql-plan-management-part-1/
Part 2 – http://avdeo.com/2011/06/07/oracle-sql-plan-management-%e2%80%93-part-2/

In part 1 we have seen information about SPM and how to automatically capture the baseline
In part 2 we have seen how to manually capture the baseline and the affect of using FIXED variable
In part 3 we will now see, how to import the baselines for SQL from some remote database and also how to set a specific plan for a query using hint and then creating baseline for that

Importing baseline from remote database to our database

Lets say we have a database and a table T in the database.

SQL> desc T
 Name                                      Null?    Type
 ----------------------------------------- -------- ----------------------------
 COL1                                               NUMBER
 COL2                                               VARCHAR2(50)
 COL3                                               DATE

No Index exists on this table.

SQL> select count(1) from T;

  COUNT(1)
----------
   2097156

SQL> select col1, count(1) from T group by col1;

      COL1   COUNT(1)
---------- ----------
         1          1
         2          3
         3    2097152

SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1048K|    11M|  1424   (6)| 00:00:18 |
|*  1 |  TABLE ACCESS FULL| T    |  1048K|    11M|  1424   (6)| 00:00:18 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

13 rows selected.

SQL>

Lets create a baseline for this query manually.

Find the SQL ID using

SQL>select sql_id from v$sql where sql_text = 'select * from T where col1 = 1';

SQL_ID
-------------
5pvxxjg6n7mrp

SQL>

DECLARE
  l_plans_loaded  PLS_INTEGER;
BEGIN
  l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(
    sql_id => '5pvxxjg6n7mrp');
END;
/

SQL> select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines where signature IN (SELECT exact_matching_signature FROM v$sql WHERE sql_id='&SQL_ID');
Enter value for sql_id: 5pvxxjg6n7mrp
old   1: select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines where signature IN (SELECT exact_matching_signature FROM v$sql WHERE sql_id='&SQL_ID')
new   1: select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines where signature IN (SELECT exact_matching_signature FROM v$sql WHERE sql_id='5pvxxjg6n7mrp')

SQL_HANDLE                     PLAN_NAME                      ENA ACC FIX
------------------------------ ------------------------------ --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  YES YES NO

SQL>

So now the baseline is created for this SQL.

If we check the plan it will show us Full Table Scan.

Now lets create an index.

SQL> create index T_IDX on T(col1);

Index created.

SQL> exec DBMS_STATS.GATHER_TABLE_STATS(OWNNAME=> '&owner', TABNAME => '&tabname', DEGREE => 6, GRANULARITY => 'ALL' ,CASCADE => TRUE , METHOD_OPT => 'FOR ALL INDEXED COLUMNS SIZE 254');
Enter value for owner: ADVAITD_DBA
Enter value for tabname: T

PL/SQL procedure successfully completed.

SQL>

lets see the plan for same query again. Since we have created an index on col1 of table T, when using value 1 we expect an index to be used rather than FTS.

SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |  1048K|    11M|  1424   (6)| 00:00:18 |
|*  1 |  TABLE ACCESS FULL| T    |  1048K|    11M|  1424   (6)| 00:00:18 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920f94ecae5c" used for this statement

17 rows selected.

SQL>

Here we clearly see baseline is getting used and it still uses FTS. This is a plan stability feature in 11g. No matter what changes we do, if a baseline already exists for a query and even if you create an index, it wont use that index unless you create a new baseline for that. As you know by now, new baseline will get created automatically only if you set OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES parameters is set to true. If this is set to false you have to create baseline manaully using DBMS_SPM package as shown above.

At this point of time we have an option to create a baseline and using DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE

But instead of using that we will try to export and import a baseline from some other DB having same query and tables.

So lets say, I have another database having same table and running same query. But that query is using correct index on col1. So we will see how to get that baseline exported and imported to our DB.
In real time situation, we will get several cases where in same set of queries are running good in some other databases but thay are having bad plan in our databaes. So instead of trying to get the plan right using traditional methods, we can simply export and import the baseline of that query from some other DB where its performing better and plans will be fixed in our DB without much effort.
So on my new DB, I have a correct plan as indicated below

SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |  2359K|   139M|   307   (1)| 00:00:04 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |  2359K|   139M|   307   (1)| 00:00:04 |
|*  2 |   INDEX RANGE SCAN          | T_IDX |  2379K|       |    47   (3)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920fae82cf72" used for this statement

18 rows selected.

SQL>

Above query is using the baseline SYS_SQL_PLAN_1d83920fae82cf72

We will export and import this baseline in our database now. Following steps needs to be done to export and import the baseline

1) Create a staging table

exec DBMS_SPM.CREATE_STGTAB_BASELINE('STGTAB', 'ADVAITD_DBA');

STGTAB –> Name of staging table. You can give any name which is not used till now. A table with this name will get created.
ADVAITD_DBA –> Owner of the table. Table will get created in this schema.

2) Pack baseline into this table

declare
x number;
begin
x := DBMS_SPM.PACK_STGTAB_BASELINE('STGTAB', 'ADVAITD_DBA', sql_handle => 'SYS_SQL_1447ba3a1d83920f', plan_name => 'SYS_SQL_PLAN_1d83920fae82cf72' );
end;
/

3) Export the staging table from source DB where you have baseline packed
exp userid=advaitd_dba@new_db tables=’STGTAB’ file=STGTAB.dmp log=STGTAB.log

advaitd.desktop$ exp userid=advaitd_dba@new_db tables='STGTAB' file=STGTAB.dmp log=STGTAB.log

Export: Release 10.2.0.2.0 - Production on Sun Aug 7 06:15:39 2011

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Password:

Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
With the Partitioning, Oracle Label Security, OLAP, Data Mining
and Real Application Testing options
Export done in US7ASCII character set and AL16UTF16 NCHAR character set
server uses UTF8 character set (possible charset conversion)

About to export specified tables via Conventional Path ...
. . exporting table                         STGTAB          1 rows exported
Export terminated successfully without warnings.
advaitd.desktop$

4) Import the table into the DB where you want to put the baseline
imp userid=advaitd_dba@our_db file=STGTAB.dmp log=STGTAB.log full=y

advaitd.desktop$ imp userid=advaitd_dba@our_db file=STGTAB.dmp log=STGTAB.log full=y

Import: Release 10.2.0.2.0 - Production on Sun Aug 7 06:20:54 2011

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Password:

Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
With the Partitioning, Oracle Label Security, OLAP, Data Mining
and Real Application Testing options

Export file created by EXPORT:V10.02.01 via conventional path
import done in US7ASCII character set and AL16UTF16 NCHAR character set
import server uses UTF8 character set (possible charset conversion)
. importing ADVAITD_DBA's objects into ADVAITD_DBA
. importing ADVAITD_DBA's objects into ADVAITD_DBA
. . importing table                       "STGTAB"          1 rows imported
Import terminated successfully without warnings.
advaitd.desktop$

5) Unpack the staging table

declare
x number;
begin
x := DBMS_SPM.UNPACK_STGTAB_BASELINE('STGTAB', 'ADVAITD_DBA');
end;
/

Now we can see 2 baseline.
- One baseline which was already exisitng and doing FTS
- One we imported just now which is using index

SQL> select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines where signature IN (SELECT exact_matching_signature FROM v$sql WHERE sql_id='&SQL_ID');
Enter value for sql_id: 5pvxxjg6n7mrp
old   1: select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines where signature IN (SELECT exact_matching_signature FROM v$sql WHERE sql_id='&SQL_ID')
new   1: select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines where signature IN (SELECT exact_matching_signature FROM v$sql WHERE sql_id='5pvxxjg6n7mrp')

SQL_HANDLE                     PLAN_NAME                      ENA ACC FIX
------------------------------ ------------------------------ --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  YES YES NO
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  YES YES NO

Now, if we check the plan for same query, we should be able to see index getting used and it will use the plan SYS_SQL_PLAN_1d83920fae82cf72 which we imported from other DB

SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |  1048K|    11M|    46   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |  1048K|    11M|    46   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T_IDX |  1048K|       |    21   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920fae82cf72" used for this statement

18 rows selected.

SQL>

Creating baseline for user created plans

Lets go back to original situation where query on the table is doing a FTS. At this point instead of getting a profile from some other DB, lets create a correct profile using hints.

I am flushing the shared_pool so as to remove any exising plans from shared_pool

SQL> alter system flush shared_pool;

System altered.

SQL> /

System altered.

SQL>

Lets use Index hint here and see if the query uses index.

SQL> select /*+ index(T_IDX T) +*/ * from T where col1 = 1;

      COL1 COL2                                               COL3
---------- -------------------------------------------------- -----------
         1                                                    10-JUL-2011

SQL> explain plan for
  2  select /*+ index(T_IDX T) +*/ * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |  1048K|    11M|   465   (2)| 00:00:06 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |  1048K|    11M|   465   (2)| 00:00:06 |
|*  2 |   INDEX RANGE SCAN          | T_IDX |  1048K|       |   209   (2)| 00:00:03 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

14 rows selected.

SQL>

So using a hint serve the purpose.
In real life situation SQLs are usually part of modules and codes. So using hint at SQL level, its not possible to implement the hint in code. Also, using hints in SQL at code level is not a good practice for obvious reason.

If we create a baseline based on hints than baseline will be automatically used for that SQL and plan will be same as that using hint.

Procedure is simple, just use the hint and run sql couple of times. One you do that, the plan will be automatically stored in shared_pool.

Find a SQL ID

SQL> select sql_id from v$sql where sql_text like 'select /*+ index(T_IDX T) +*/ * from%';

SQL_ID
-------------
50tsvs88gx382

Use DBMS_SPM.load_plans_from_cursor_cache to create baseline from cursor cache.

DECLARE
  l_plans_loaded  PLS_INTEGER;
BEGIN
  l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(
    sql_id => '50tsvs88gx382');
END;
/

At this point we have 2 SQLs -

50tsvs88gx382 – using index hint
5pvxxjg6n7mrp – Original query without using hint. This is doing FTS

SQL> select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines ;

SQL_HANDLE                     PLAN_NAME                      ENA ACC FIX
------------------------------ ------------------------------ --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  YES YES NO
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  YES NO  NO
SYS_SQL_3f0d350a1c24a8d4       SYS_SQL_PLAN_1c24a8d4ae82cf72  YES YES NO

SQL>

SQL_HANDLE – SYS_SQL_1447ba3a1d83920f is for original query without hint. This was having only 1 baseline, but when we loaded the baseline for query 50tsvs88gx382 which is using hint, it automatically created the baseline for original query. 11g is smart enought to filter the hints and create a baseline for original query.

At this stage you have 2 option

1) Accept plan SYS_SQL_PLAN_1d83920fae82cf72 for SQL_HANDLE SYS_SQL_1447ba3a1d83920f
2) Link sql plan SYS_SQL_PLAN_1c24a8d4ae82cf72 of SQL_HANDLE SYS_SQL_3f0d350a1c24a8d4 to SQL_HANDLE SYS_SQL_1447ba3a1d83920f

You already know option 1.

Lets see option 2.

To link the plan of some other SQL_HANDLE (SYS_SQL_3f0d350a1c24a8d4) to our SQL_HANDLE (SYS_SQL_1447ba3a1d83920f), you can use same procedure dbms_spm.load_plans_from_cursor_cache

DECLARE
  l_plans_loaded  PLS_INTEGER;
BEGIN
  l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(
    sql_id => '50tsvs88gx382',
    PLAN_HASH_VALUE => 470836197,
    SQL_HANDLE => 'SYS_SQL_1447ba3a1d83920f');
END;
/

PL/SQL procedure successfully completed.

In above procedure,
- SQL_ID is the new SQL_ID having hints.
- PLAN_HASH_VALUE is the plan hash value of SQL with hints. This is the plan we want to assign to our SQL_HANDLE
- SQL_HANDLE is the name of our SQL Handle to which we want to assign the plan

After assigning the plan, you will see the status as below.

SQL> select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED from dba_sql_plan_baselines ;

SQL_HANDLE                     PLAN_NAME                      ENA ACC FIX
------------------------------ ------------------------------ --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  YES YES NO
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  YES YES NO
SYS_SQL_3f0d350a1c24a8d4       SYS_SQL_PLAN_1c24a8d4ae82cf72  YES YES NO

SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |  1048K|    11M|   465   (2)| 00:00:06 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |  1048K|    11M|   465   (2)| 00:00:06 |
|*  2 |   INDEX RANGE SCAN          | T_IDX |  1048K|       |   209   (2)| 00:00:03 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920fae82cf72" used for this statement

18 rows selected.

SQL>

Hope this helps !!

Oracle SQL Plan Management – Part 2

SQL Plan Management – Oracle Database 11g

In SPM part 1 we saw information about SPM and how to automatically capture the baseline.
In this part we will see how to manually capture the baseline and the affect of using FIXED variable.

Capturing Baseline Manually

Occasionally we have to to capture the baseline manually. This could happen while tuning the query in our database where we have optimizer_capture_sql_plan_baselines parameter set to FALSE.

We have 2 procedures available for capturing the baselines manually.

  1. LOAD_PLANS_FROM_SQLSET

  2. LOAD_PLANS_FROM_CURSOR_CACHE

Above procedures are part of DBMS_SPM packages.

LOAD_PLANS_FROM_SQLSET procedure is used mainly while doing the upgrade from Oracle database 10g to Oracle database 11g.

The procedure is to

  1. create a SQL Tuning set in 10g and pack all the SQLs in the library cache along with there plans into SQL tuning set  – Procedure DBMS_SQLTUNE.CREATE_SQLSET and DBMS_SQLTUNE.LOAD_SQLSET
  2. Then to create a staging table and load this SQL tuning set into the staging table – Procedure DBMS_SQLTUNE.CREATE_STGTAB_SQLSET and DBMS_SQLTUNE.PACK_STGTAB_SQLSET
  3. Update database to 11g
  4. Unpack the staging table into SQL Tuning set you created before – Procedure DBMS_SQLTUNE.UNPACK_STGTAB_SQLSET
  5. Create SQL Baselines from the SQL tuning set – Procedure DBMS_SPM.LOAD_PLANS_FROM_SQLSET

LOAD_PLANS_FROM_CURSOR_CACHE is used after 11g upgrade when we have optimizer_capture_sql_plan_baselines set to false and we want to create baseline for a query in order to stablize the plan.



SQL> select col1, count(1) from T group by col1;

      COL1   COUNT(1)
---------- ----------
         1          1
         2          3
         3      98304

SQL> show parameters capture

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_capture_sql_plan_baselines boolean     FALSE

SQL>

Currently we dont have any baselines in the database.


SQL> select sql_handle, plan_name, sql_text, enabled, accepted, fixed from dba_sql_plan_baselines;

no rows selected

SQL>

SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

SQL> set line 999
SQL> set pagesize 999
SQL> select * from table(dbms_xplan.display);

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 49154 |  2976K|   260   (2)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T    | 49154 |  2976K|   260   (2)| 00:00:04 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

13 rows selected.

SQL>

Lets create a baseline manually for this statement. For creating baselines manually we need to have the statement in cache first.
Lets run the statement once and find the SQL ID.


SQL> select sql_id, sql_text from v$sql where sql_text like 'select * from T where%';

SQL_ID        SQL_TEXT
------------- ------------------------------------------------------------------------------------------
5pvxxjg6n7mrp select * from T where col1 = 1

Now we will use LOAD_PLANS_FROM_CURSOR_CACHE to create a baseline

SQL> DECLARE
  2  l_plans_loaded  PLS_INTEGER;
  3  BEGIN
  4  l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(
  5  sql_id => '5pvxxjg6n7mrp');
  6  END;
  7  /

PL/SQL procedure successfully completed.

SQL> select sql_handle, plan_name, sql_text, enabled, accepted, fixed from dba_sql_plan_baselines;

SQL_HANDLE                     PLAN_NAME                      SQL_TEXT                         ENA ACC FIX
------------------------------ ------------------------------ -------------------------------- --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  select * from T where col1 = 1   YES YES NO

SQL>

So we can see that SQL Plan baseline is created as well as accepted.
We will check out the plan from baseline now, but it should be full table scan.


SQL> SET LONG 10000
SQL> select * from TABLE(DBMS_XPLAN.display_sql_plan_baseline(plan_name=>'SYS_SQL_PLAN_1d83920f94ecae5c'));

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

--------------------------------------------------------------------------------
SQL handle: SYS_SQL_1447ba3a1d83920f
SQL text: select * from T where col1 = 1
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
Plan name: SYS_SQL_PLAN_1d83920f94ecae5c
Enabled: YES     Fixed: NO      Accepted: YES     Origin: MANUAL-LOAD
--------------------------------------------------------------------------------

Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 49154 |  2976K|   260   (2)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T    | 49154 |  2976K|   260   (2)| 00:00:04 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

24 rows selected.

SQL>

Now lets create an index on the table on COL1.

SQL> create index T_IDX on T(COL1);

Index created.

SQL>

Now since the baseline is used, same query will go for a full table scan even after creating an index.


SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 49154 |  2976K|   260   (2)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T    | 49154 |  2976K|   260   (2)| 00:00:04 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920f94ecae5c" used for this statement

17 rows selected.

SQL>

So as you can see we have a baseline SYS_SQL_PLAN_1d83920f94ecae5c getting used for this query.

Lets go ahead and try creating another baseline for this SQL. May be this time we will get better plan.

SQL ID remains the same.


SQL> DECLARE
  2  l_plans_loaded  PLS_INTEGER;
  3  BEGIN
  4  l_plans_loaded := DBMS_SPM.load_plans_from_cursor_cache(
  5  sql_id => '5pvxxjg6n7mrp');
  6  END;
  7  /

PL/SQL procedure successfully completed.

SQL> select sql_handle, plan_name, sql_text, enabled, accepted, fixed from dba_sql_plan_baselines;

SQL_HANDLE                     PLAN_NAME                      SQL_TEXT                         ENA ACC FIX
------------------------------ ------------------------------ -------------------------------- --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  select * from T where col1 = 1   YES YES NO
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  select * from T where col1 = 1   YES NO  NO

And yes, we can see Oracle has created another baseline here. But ACCEPTED is still NO for the new plan.
This is because we haven’t evaluated the plan yet. If we would have set optimizer_capture_sql_plan_baselines parameter to true, it would have automatically captured and made ACCEPTED=YES after doing automatic evaluation.
Since this is false, we have to evaluate whether new plan is better or not. This process of evaluating new baseline is called evolving.

Evolving a plan actually compares the cost and several other factors like DISK_READS, BUFFER_GETS, ELAPSED_TIME etc for all the plans that are available in DBA_SQL_PLAN_BASELINES table for that particular SQL HANDLE.
So in our case when we evolve the plan it will compare plan SYS_SQL_PLAN_1d83920f94ecae5c and SYS_SQL_PLAN_1d83920fae82cf72

Just to be certain we will display the plan SYS_SQL_PLAN_1d83920fae82cf72



SQL> select * from TABLE(DBMS_XPLAN.display_sql_plan_baseline(plan_name=>'SYS_SQL_PLAN_1d83920fae82cf72'));

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

--------------------------------------------------------------------------------
SQL handle: SYS_SQL_1447ba3a1d83920f
SQL text: select * from T where col1 = 1
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
Plan name: SYS_SQL_PLAN_1d83920fae82cf72
Enabled: YES     Fixed: NO      Accepted: NO      Origin: AUTO-CAPTURE
--------------------------------------------------------------------------------

Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       | 49154 |  2976K|     6   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     | 49154 |  2976K|     6   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T_IDX | 49154 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

25 rows selected.

SQL>

So it is indeed using the index on the table.

Here there are 2 things again.

  1. Evolving the plan automatically – Using procedure DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE
  2. Manually accepting the plan – Using procedure ALTER_SQL_PLAN_BASELINE setting ACCEPTED=>YES

In the first method optimizer will evaluate based on all the factors (we will see those factors below) and “selects” the best plan. I have highlighted select here because optimizer is going to select from the set of existing plans, its not going to parse and create a whole other plan.
So its upto the optimizer to decide which plan is best among the available plan.

In method 2, we can decide and select which ever plan we think is correct. This based on our knowledge of the type of SQL we are running and what resources we have with us.

Can we have 2 plans in ACCEPTED status for a query?

Yes we can.

Which plan optimizer will choose out of the two?

Always the best among the two. If more than 1 plan is ACCEPTED than optimizer will choose the best plan to be used for the query.
Example if we use a query “select * from T where col = :B1″
Here we have used a bind variable. Lets say we have 2 plans with us right now
Plan 1) Full table scan
Plan 2) Index Range scan

Which plan optimizer will choose depends upon the value of bind variable.
Yes, in 11g we have something called “Adaptive Cursor Sharing”, but this is little off the topic. We will cover “Adaptive Cursor Sharing” in different article.

So if we supply value as 1 or 2 and since there is only 1 row with col1=1 and 3 rows with col1=2, optimizer will use Plan 2) Index Range Scan.
If we supply value as 3, optimizer will use Plan 1) Full table scan.

So imagine a case where we have 5-6 plans based on selectivity of different tables involved in a complex query, in that case its a good idea to keep all those plans as ACCEPTED. Optimizer will always choose the best among those based on selectivity of the data.

We have seen evolving the plan in Part 1 – http://avdeo.com/2011/06/02/oracle-sql-plan-management-part-1/
Lets set ACCEPTED=TRUE manually now.


SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2    l_plans_altered  PLS_INTEGER;
  3  BEGIN
  4    l_plans_altered := DBMS_SPM.alter_sql_plan_baseline(
  5      sql_handle      => 'SYS_SQL_1447ba3a1d83920f',
  6      plan_name       => 'SYS_SQL_PLAN_1d83920fae82cf72',
  7      attribute_name  => 'ACCEPTED',
  8      attribute_value => 'YES');
  9
 10    DBMS_OUTPUT.put_line('Plans Altered: ' || l_plans_altered);
 11  END;
 12  /
Plans Altered: 1

PL/SQL procedure successfully completed.

SQL>

SQL> select sql_handle, plan_name, sql_text, enabled, accepted, fixed from dba_sql_plan_baselines;

SQL_HANDLE                     PLAN_NAME                      SQL_TEXT                         ENA ACC FIX
------------------------------ ------------------------------ -------------------------------- --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  select * from T where col1 = 1   YES YES NO
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  select * from T where col1 = 1   YES YES NO

SQL>

Lets check the explain plan for value 1.


SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       | 49154 |  2976K|    56   (2)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     | 49154 |  2976K|    56   (2)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T_IDX | 49154 |       |    10   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920fae82cf72" used for this statement

18 rows selected.

SQL>

So its using SYS_SQL_PLAN_1d83920fae82cf72 baselines.

What is the affect of using FIXED=YES ?

Fixed attribute will fix the plan for a query. So even if we have a better plan, the query will still not be using it. It is like disabling (ENABLED=NO, ACCEPTED=NO) all the plans for that SQL Handle.

Let make FIXED=YES for the first baseline (SYS_SQL_PLAN_1d83920f94ecae5c) which does full table scan.
To change this attribute we can again use DBMS_SPM.ALTER_SQL_PLAN_BASELINE procedure



SQL> set serveroutput on
SQL> DECLARE
  2    l_plans_altered  PLS_INTEGER;
  3  BEGIN
  4    l_plans_altered := DBMS_SPM.alter_sql_plan_baseline(
  5      sql_handle      => 'SYS_SQL_1447ba3a1d83920f',
  6      plan_name       => 'SYS_SQL_PLAN_1d83920f94ecae5c',
  7      attribute_name  => 'FIXED',
  8      attribute_value => 'YES');
  9
 10    DBMS_OUTPUT.put_line('Plans Altered: ' || l_plans_altered);
 11  END;
 12  /
Plans Altered: 1

PL/SQL procedure successfully completed.

SQL>

SQL> select sql_handle, plan_name, sql_text, enabled, accepted, fixed from dba_sql_plan_baselines;

SQL_HANDLE                     PLAN_NAME                      SQL_TEXT                                           ENA ACC FIX
------------------------------ ------------------------------ -------------------------------------------------- --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  select * from T where col1 = 1                     YES YES YES
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  select * from T where col1 = 1                     YES YES NO

SQL>

Lets check out the explain plan for “select * from T where col1 = 1″ query


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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      | 49154 |  2976K|   260   (2)| 00:00:04 |
|*  1 |  TABLE ACCESS FULL| T    | 49154 |  2976K|   260   (2)| 00:00:04 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920f94ecae5c" used for this statement

17 rows selected.

SQL>

So if you see now, same query which was earlier using index scan has now shifted to full table scan. Thats the magic of SPM !!
I would recommend not using FIXED attribute at all. You are just killing the functionality provided by 11g.

However, if we make both plans as FIXED then ??


set serveroutput on                                           
DECLARE                                                       
  l_plans_altered  PLS_INTEGER;                               
BEGIN                                                         
  l_plans_altered := DBMS_SPM.alter_sql_plan_baseline(        
    sql_handle      => 'SYS_SQL_1447ba3a1d83920f',            
    plan_name       => 'SYS_SQL_PLAN_1d83920fae82cf72',       
    attribute_name  => 'FIXED',                               
    attribute_value => 'YES');                                
                                                              
  DBMS_OUTPUT.put_line('Plans Altered: ' || l_plans_altered); 
END;                                                          
/                                                             

SQL> select sql_handle, plan_name, sql_text, enabled, accepted, fixed from dba_sql_plan_baselines;

SQL_HANDLE                     PLAN_NAME                      SQL_TEXT                                           ENA ACC FIX
------------------------------ ------------------------------ -------------------------------------------------- --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  select * from T where col1 = 1                     YES YES YES
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  select * from T where col1 = 1                     YES YES YES

SQL> explain plan for
  2  select * from T where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       | 49154 |  2976K|     6   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     | 49154 |  2976K|     6   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T_IDX | 49154 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920fae82cf72" used for this statement

18 rows selected.

SQL>

It will again go back to best among the fixed plans.

In the next part, I will cover how to transfer the baseline from one database to another and also how to set a specific plan for a query using hint and then creating baseline for that.

Hope this helps !!

 

Oracle SQL Plan Management – Part 1

SQL Plan Management – Oracle Database 11g

SQL Plan Management or SPM is a new feature introduced in 11g. We will take a detailed look at SPM in the following sessions.
Before starting with practical example lets see some theory.

What is SQL Plan Management?

SPM is a new feature introduced in Oracle database 11g which stores the baseline (Plans + Hints) inside database.
SPM allows an Oracle DBA to capture and preserve the most efficient execution plans for any SQL statement, thus limiting the impact of refreshed optimizer statistics, changes to existing applications, and even upgraded database versions

Why do we need SPM?

SPM is basically used for plan stability. If you have a database environment with several queries running, you always want your queries to run the way they are running irrespective of the changes that you make in your environment.
Example, If you upgrade your database, your queries might behave differently because of the change in optimizer or change in environment and at initial run you have to fix many queries in your production DB because of the upgrade or change in the env.
To avoid this, SPM is introduce to give you plan stability. So even after change in the environment, your queries will use same plan as it was before. More plans (Better or worse) could exists in the new environment, but optimizer is not allowed to use those plans without you confirming the plan change.

What was wrong with outlines?

Well, outlines are more about fixing the plan for a query that optimizer will use. So if there is change in environment, the plan would still be the same.
There are certain situations where you want the plan to change with change in lets say data. What if your data gets skewed over time.

Intially you had 100 of entries for month “DEC” out of total 150 entries. So it was using FTS. Now with increase in the data, there are 1 million records and entries for month “DEC” are around 1000. In that case it makes sense for optimizer to change plan and start using Index scan instead of FTS.
But since you used outline, you are forcing optimizer to go for a FTS. This is just an example situation and things can change in your environment and so outline is not a very good approach for such situations.

What was wrong with profile?

Profile is another feature introduced in 10g and can be implemented at SQL level. Profiles are better than outlines in that they are not fixing a plan through out the life of SQL. With change in the environment the plans can change even if the profile is used.

So whats the difference between profile and SQL plan baselines?

Profile is more of a advisors. They give advice to optimizer while executing the query. They provide optimizer with all the precise estimates. Profiles are more abount correcting optimizer to use correct plan when the underlying data is skewed and changed drastically.
The goal is to create best execution plan for the SQL by giving the very precise data to the optimizer. Its the optimizer who will decide what should be the explain plan based on the information/hints it has received from profile.

SQL Plan baselines works differently. It enforces the plan to be used for a SQL. Baseline does not provide and estimates or it does not help optimizer in anyway. It just tells optimizer to ignore everything and use the plan that we are giving you.
Optimizer just followes the plan provided by baselines. So here baseline is the driving mechanism for getting the correct plan.

Finally how baselines are different then outline then?

Well, baseline always keeps the optimized plans for your SQL. If there is a change in the environment you will have a new baseline created for the same SQL. Oracle will stop that baseline in the baseline history.
Depending on the baseline parameters setting in your environment, new plan will be automatically used or you have to evolve the new plan for optimizer to use it.

So in a way SQL baselines are combination of outlines and profiles. It gives the stability of plan similar to outlines and it also allows capturing better plans in case the environment changes.

Not only that, SQL Baselines give the complete control to the DBA on

  1. Whether to capture the new and better plans?
  2. Whether to use the new plans automaticallly without DBA’s intervention.

So DBAs have complete control of the environment now. Far better than profiles and outlines.

SQL Plan Baseline Parameters:

Before we check how to use SQL Plan baselines, lets consider the significance of 2 important baseline parameters

1) optimizer_capture_sql_plan_baselines – Default “FALSE”

This parameter is responsible for automatic capturing the baselines for SQLs. If we set this parameter to TRUE we are asking oracle to automatically gather the baselines for the SQL.
When you run the query for the first time (and parsed version is not present in shared_pool), oracle consider that as a fresh new query and does not create a baseline.
When you run the query for second time, oracle will consider the query as repetative and will automatically create the baseline.
These baseline are stored in DBA_SQL_PLAN_BASELINES table.

If this parameter is set to FALSE, then we (DBA) has to create baselines for the SQL manually. There are 2 procedure available for creating baselines manually.

1) Using DBMS_SPM.LOAD_PLANS_FROM_SQLSET

This procedure is usually used when we create SQL tuning set and store our SQLs into the tuning sets. Normally done before we upgrade the database to 11g. In our 10g database we create SQL tuning set and store all our SQL. Once we upgrade to 11g, we can create baselines for all our plans in SQL tuning set. That why what ever plans were effective in 10g, same will be used in 11g and there wont be any plan flips.

2) Using DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE

This is used when we want to fix one of the SQL currently running in our 11g database. We just load the plans from cursor cache (shared_pool) and create baseline out of that. We need to give SQL ID as input to this procedure.

We will see how to create baseline using manual method at later point of time.

2) optimizer_use_sql_plan_baselines – Default “TRUE”

This parameter will allow optimizer to use the baselines present in DBA_SQL_PLAN_BASELINES table. If you set this parameter to FALSE, then your 11g DB will start behaving same as 10g DB.
If there is any change in environment then it might flip the plan. Keeping this parameter TRUE is important in 11g.

How to use SQL Plan Management baselines ?

Lets take an example in a test database.

Table T with 1540 records.

SQL> select count(1) from t;

  COUNT(1)
----------
      1540

Data is skewed and distribution is as given below.

SQL> select col1, count(1) from t group by col1;

      COL1   COUNT(1)
---------- ----------
         1          1
         2          3
         3       1536

Gather the stats on the table

SQL> exec dbms_stats.gather_table_stats(OWNNAME=> 'ADVAITD_DBA',TABNAME => 'T', DEGREE => 6, GRANULARITY => 'ALL' ,CASCADE => TRUE , METHOD_OPT => 'FOR ALL INDEXED COLUMNS SIZE 254');

SQL>

Currently I dont have any baseline.

SQL> select count(1) from dba_sql_plan_baselines;

  COUNT(1)
----------
         0

SQL>

My baseline parameters setting is as below.

SQL> show parameters baselines

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_capture_sql_plan_baselines boolean     FALSE
optimizer_use_sql_plan_baselines     boolean     TRUE
SQL>

Let us first consider the auto capture utility for baselines.

AUTO Capture of baseline

SQL> alter session set optimizer_capture_sql_plan_baselines=TRUE;

Session altered.

SQL>

As mention earlier, we need to run the query 2 times in order to automatically create the baseline.

SQL> select * from t where col1 = 1;

      COL1 COL2                                               COL3
---------- -------------------------------------------------- -----------
         1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 01-JUN-2011

SQL> select * from t where col1 = 1;

      COL1 COL2                                               COL3
---------- -------------------------------------------------- -----------
         1 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 01-JUN-2011

If we check DBA_SQL_PLAN_BASELINES we will see a sql baseline created.

SQL> select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED
  2  from dba_sql_plan_baselines
  3  WHERE sql_text like 'select * from t%';

SQL_HANDLE                     PLAN_NAME                      ENA ACC FIX
------------------------------ ------------------------------ --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  YES YES NO

Turning off auto SQL plan baseline capture

SQL> alter session set optimizer_capture_sql_plan_baselines=FALSE;

Session altered.

SQL>

Following statement gives the plan stored in the baseline. DBMS_XPLAN has a new procedure DISPLAY_SQL_PLAN_BASELINE which will display the baseline.

SQL> set line 999
SQL> set pagesize 999
SQL> select * from table(dbms_xplan.display_sql_plan_baseline(plan_name=>'SYS_SQL_PLAN_1d83920f94ecae5c'));

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

--------------------------------------------------------------------------------
SQL handle: SYS_SQL_1447ba3a1d83920f
SQL text: select * from t where col1 = 1
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
Plan name: SYS_SQL_PLAN_1d83920f94ecae5c
Enabled: YES     Fixed: NO      Accepted: YES     Origin: AUTO-CAPTURE
--------------------------------------------------------------------------------

Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    15 |   735 |     9   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |    15 |   735 |     9   (0)| 00:00:01 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

24 rows selected.

SQL>

Origin: AUTO-CAPTURE shown above tell us that this baseline is captured automatically.

Lets now create an index and gather stats over index.

SQL> create index t_idx on t(col1);

Index created.

SQL> exec DBMS_STATS.GATHER_INDEX_STATS(OWNNAME => 'ADVAITD_DBA', INDNAME=>'T_IDX');

Run the same query now, since the index is created, we expect the query to use the index.

SQL> explain plan for
  2  select * from t where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1601196873

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |    15 |   735 |     9   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| T    |    15 |   735 |     9   (0)| 00:00:01 |
--------------------------------------------------------------------------

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

   1 - filter("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920f94ecae5c" used for this statement

17 rows selected.

SQL>

The reason we are seeing full table scan is because of the NOTE at the end, which says “SQL plan baseline “SYS_SQL_PLAN_1d83920f94ecae5c” used for this statement”

Since we have a baseline created for this SQL, it will not allow the plan to be changed. This is the kind of stability that SQL Plan baseline gives.
But using an index will be beneficial in our case.

If we check DBA_SQL_PLAN_BASELINES we can see a new plan has been created (PLAN_NAME = SYS_SQL_PLAN_1d83920fae82cf72), but it is not yet ACCEPTED. The plan is enabled though.

SQL> select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED
  2  from dba_sql_plan_baselines
  3  WHERE sql_text like 'select * from t%';

SQL_HANDLE                     PLAN_NAME                      ENA ACC FIX
------------------------------ ------------------------------ --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  YES YES NO
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  YES NO  NO

We can check what the new plan looks like using dbms_xplan.display_sql_plan_baseline

SQL> select * from table(dbms_xplan.display_sql_plan_baseline(plan_name=>'SYS_SQL_PLAN_1d83920fae82cf72'));

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

--------------------------------------------------------------------------------
SQL handle: SYS_SQL_1447ba3a1d83920f
SQL text: select * from t where col1 = 1
--------------------------------------------------------------------------------

--------------------------------------------------------------------------------
Plan name: SYS_SQL_PLAN_1d83920fae82cf72
Enabled: YES     Fixed: NO      Accepted: NO      Origin: AUTO-CAPTURE
--------------------------------------------------------------------------------

Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |    15 |   735 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |    15 |   735 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T_IDX |     6 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

25 rows selected.

SQL>

As seen above, new plan uses index. Lets evolve this plan now.
Evolving a plan includes evaluating the cost of the plan and accepting if the plan seems to be better than all accepted plan for this query.

SQL> SELECT DBMS_SPM.evolve_sql_plan_baseline(sql_handle => 'SYS_SQL_1447ba3a1d83920f') from dual;

DBMS_SPM.EVOLVE_SQL_PLAN_BASELINE(SQL_HANDLE=>'SYS_SQL_1447BA3A1D83920F')
--------------------------------------------------------------------------------

-------------------------------------------------------------------------------
                        Evolve SQL Plan Baseline Report
-------------------------------------------------------------------------------

Inputs:
-------
  SQL_HANDLE = SYS_SQL_1447ba3a1d83920f
  PLAN_NAME  =
  TIME_LIMIT = DBMS_SPM.AUTO_LIMIT
  VERIFY     = YES
  COMMIT     = YES

Plan: SYS_SQL_PLAN_1d83920fae82cf72
-----------------------------------
  Plan was verified: Time used .01 seconds.
  Passed performance criterion: Compound improvement ratio >= 7.33
  Plan was changed to an accepted plan.

                      Baseline Plan      Test Plan     Improv. Ratio
                      -------------      ---------     -------------
  Execution Status:        COMPLETE       COMPLETE
  Rows Processed:                 1              1
  Elapsed Time(ms):               0              0
  CPU Time(ms):                   0              0
  Buffer Gets:                   22              3              7.33
  Disk Reads:                     0              0
  Direct Writes:                  0              0
  Fetches:                        0              0
  Executions:                     1              1

-------------------------------------------------------------------------------
                                 Report Summary
-------------------------------------------------------------------------------
Number of SQL plan baselines verified: 1.
Number of SQL plan baselines evolved: 1.

Sometimes, your plan may not get evolved because oracle see that there are other already ACCEPTED plans which are better than the plan you are trying to evolve.
But if you know your plan will be better and still want to deploy the same, you can do so by manually changing the attributes ACCEPTED and ENABLED as shown below.

SET SERVEROUTPUT ON
DECLARE
  l_plans_altered  PLS_INTEGER;
BEGIN
  l_plans_altered := DBMS_SPM.alter_sql_plan_baseline(
    sql_handle      => 'SYS_SQL_1447ba3a1d83920f',
    plan_name       => 'SYS_SQL_PLAN_1d83920fae82cf72',
    attribute_name  => 'ENABLED',
    attribute_value => 'YES');

  DBMS_OUTPUT.put_line('Plans Altered: ' || l_plans_altered);
END;
/

SET SERVEROUTPUT ON
DECLARE
  l_plans_altered  PLS_INTEGER;
BEGIN
  l_plans_altered := DBMS_SPM.alter_sql_plan_baseline(
    sql_handle      => 'SYS_SQL_1447ba3a1d83920f',
    plan_name       => 'SYS_SQL_PLAN_1d83920fae82cf72',
    attribute_name  => 'ACCEPTED',
    attribute_value => 'YES');

  DBMS_OUTPUT.put_line('Plans Altered: ' || l_plans_altered);
END;
/

You can also set the value of attribute FIXED using the above function. Here is the meaning of ENABLED, ACCPETED and FIXED

ENABLED   – ‘YES’ means the plan is available for use by the optimizer. It may or may not be used depending on accepted status.
ACCPETED – ‘YES’ means the plan will be used by optimizer while running the query. ‘NO’ means optimizer will not use the plan.
FIXED        – ‘YES’ means the SQL plan baseline is not evolved over time. A fixed plan takes precedence over a non-fixed plan.

Once you evolve the plan, you can see that plan is ACCEPTED now.

SQL> select SQL_HANDLE, PLAN_NAME, ENABLED, ACCEPTED, FIXED
  2  from dba_sql_plan_baselines
  3  WHERE sql_text like 'select * from t%';

SQL_HANDLE                     PLAN_NAME                      ENA ACC FIX
------------------------------ ------------------------------ --- --- ---
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920f94ecae5c  YES YES NO
SYS_SQL_1447ba3a1d83920f       SYS_SQL_PLAN_1d83920fae82cf72  YES YES NO

Now if you run the explain plan you can see Index T_IDX is getting used.

SQL> explain plan for
  2  select * from t where col1 = 1;

Explained.

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

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 470836197

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |    15 |   735 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| T     |    15 |   735 |     1   (0)| 00:00:01 |
|*  2 |   INDEX RANGE SCAN          | T_IDX |     6 |       |     1   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

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

   2 - access("COL1"=1)

Note
-----
   - SQL plan baseline "SYS_SQL_PLAN_1d83920fae82cf72" used for this statement

18 rows selected.

SQL>

Hope this helps.

Part 2 can be viewed at -http://avdeo.com/2011/06/07/oracle-sql-plan-management-%E2%80%93-part-2/

References:

http://download.oracle.com/docs/cd/B28359_01/server.111/b28274/optplanmgmt.htm

http://www.comp.dit.ie/btierney/oracle11gdoc/appdev.111/b28419/d_spm.htm

Creating Standby database using Active Duplication – Oracle Database 11g

Introduction:

Oracle database 11g introduced a new feature called Active database duplication. Using this feature you can create a new database (primary/standby) from your current running database. This feature does not needs any backup to be taken, nor we have to do any restore.

Creating active duplication is a RMAN feature and command for creating active duplication comes with various options.

Following is the command used for active duplication

duplicate target database to <DB_NAME> from active database;

Following are the various arguments that we can pass to this command

FROM ACTIVE DATABASE: (This is supplied if we want to do active database duplication)

Specifies that the files for the standby database should be provided directly from the source database and not from a backup of the source database

NOFILENAMECHECK:

Prevents RMAN from checking whether datafiles of the source database share the same names as the standby database files that are in use.

The NOFILENAMECHECK option is required when the standby and primary datafiles and online redo logs have identical filenames. Thus, if you want the duplicate database filenames to be the same as the source database filenames, and if the databases are in different hosts, then you must specify NOFILENAMECHECK

SPFILE:

Copies the server parameter file from the source database to the operating system-specific default location for this file on the standby database.

RMAN uses the server parameter file to start the auxiliary instance for standby database creation. Any remaining options of the DUPLICATE command are processed after the database instance is started with the server parameter file.

If you execute DUPLICATE with the SPFILE clause, then the auxiliary instance must already be started with a text-based initialization parameter file. In this case, the only required parameter in the temporary initialization parameter file is DB_NAME, which can be set to any arbitrary value. RMAN copies the binary server parameter file, modifies the parameters based on the settings in the SPFILE clause, and then restarts the standby instance with the server parameter file. When you specify SPFILE, RMAN never uses the temporary text-based initialization parameter file to start the instance.

If FROM ACTIVE DATABASE is specified on DUPLICATE, then a server parameter file must be in use by the source database instance. If FROM ACTIVE DATABASE is not specified on DUPLICATE, then RMAN restores a backup of the server parameter file to the standby database.

PARAMETER_VALUE_CONVERT:

Replaces the first string with the second string in all matching initialization parameter values. Note that DB_FILE_NAME_CONVERT and LOG_FILE_NAME_CONVERT are exceptions to this rule and are not affected.

You can use PARAMETER_VALUE_CONVERT to set a collection of initialization parameter values and avoid explicitly setting them all. For example, if the source database uses disk group +ALPHA while the standby database will use +BETA, then you could modify all parameters that refer to these disk groups by specifying SPFILE PARAMETER_VALUE_CONVERT (‘+ALHPA’,'+BETA’).

DORECOVER:

Specifies that RMAN should recover the standby database after creating it. If you specify an untilClause, then RMAN recovers to the specified SCN or time and leaves the database mounted.

RMAN leaves the standby database mounted after media recovery is complete, but does not place the standby database in manual or managed recovery mode. After RMAN creates the standby database, you must resolve any gap sequence before placing it in manual or managed recovery mode, or opening it in read-only mode.

For more details on arguments to be supplied check – http://download.oracle.com/docs/cd/B28359_01/backup.111/b28273/rcmsynta020.htm

Following is one example where I created a standby database using active database duplication.

Pre-requisite:

We should have a primary database up and running
pfile, spfile and listener.ora file should be available for both primary and standby
All the relevent directories for datafiles and diagnostic dest should be present
Password file for standby should be present
Entry should exist in /etc/oratab for both primary and standby

Steps for active database duplication

Step 1) nomount the standby database

sqlplus "/as sysdba"

SQL*Plus: Release 11.1.0.7.0 - Production on Wed May 4 06:53:47 2011

Copyright (c) 1982, 2008, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> startup nomount
ORA-32004: obsolete and/or deprecated parameter(s) specified
ORACLE instance started.

Total System Global Area 2321612800 bytes
Fixed Size                  2174128 bytes
Variable Size            1198015312 bytes
Database Buffers         1073741824 bytes
Redo Buffers               47681536 bytes
SQL>


Step 2) Check the unique name for standby database

SQL> show parameters uniq

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
db_unique_name                       string      orcl_a

Step 3) Start active duplication

rman target sys/<password>@orcl1 auxiliary=sys/<password>@orcl1_a

Recovery Manager: Release 11.1.0.7.0 - Production on Wed May 4 07:01:22 2011

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

connected to target database: ORCL1 (DBID=998984585)
connected to auxiliary database: ORCL1 (not mounted)

RMAN> duplicate target database to orcl1 from active database nofilenamecheck; --> This will create a new primary database again
RMAN> duplicate target database for standby from active  database nofilenamecheck; --> This will create a new standby database

Starting Duplicate Db at 04-MAY-11
allocated channel: ORA_AUX_DISK_1
channel ORA_AUX_DISK_1: SID=1122 device type=DISK

contents of Memory Script:
{
   backup as copy reuse
   file  '/opt/app/oracle/product/11.1.0.7/A24db/dbs/orapworcl1' auxiliary format
 '/opt/app/oracle/product/11.1.0.7/A24db/dbs/orapworcl1'   ;
}
executing Memory Script

Starting backup at 04-MAY-11
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=1109 device type=DISK
Finished backup at 04-MAY-11

contents of Memory Script:
{
   backup as copy current controlfile for standby auxiliary format  '/ctl-01/databases/orcl1/control.ctl';
   restore clone controlfile to  '/ctl-02/databases/orcl1/control.ctl' from
 '/ctl-01/databases/orcl1/control.ctl';
   sql clone 'alter database mount standby database';
}
executing Memory Script

Starting backup at 04-MAY-11
using channel ORA_DISK_1
channel ORA_DISK_1: starting datafile copy
copying standby control file
output file name=/opt/app/oracle/product/11.1.0.7/A24db/dbs/snapcf_orcl1.f tag=TAG20110504T072433 RECID=2 STAMP=750237875
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:07
Finished backup at 04-MAY-11

Starting restore at 04-MAY-11
using channel ORA_AUX_DISK_1

channel ORA_AUX_DISK_1: copied control file copy
Finished restore at 04-MAY-11

sql statement: alter database mount standby database

contents of Memory Script:
{
   set newname for tempfile  1 to
 "/fs-b01-a/databases/orcl1/temp-01.dbf";
   set newname for tempfile  2 to
 "/fs-a01-a/databases/orcl1/dba_temp-01.dbf";
   set newname for tempfile  3 to
 "/fs-a01-a/databases/orcl1/temp-02.dbf";
   switch clone tempfile all;
   set newname for datafile  1 to
 "/fs-b01-a/databases/orcl1/system-01.dbf";
   set newname for datafile  2 to
 "/fs-a01-a/databases/orcl1/undo_t1-01.dbf";
   set newname for datafile  3 to
 "/fs-a01-a/databases/orcl1/sysaux-01.dbf";
   set newname for datafile  4 to
 "/fs-a01-a/databases/orcl1/administrator-01.dbf";
   set newname for datafile  5 to
 "/fs-b01-a/databases/orcl1/administrator_idx-01.dbf";
   set newname for datafile  6 to
 "/fs-a01-a/databases/orcl1/replication-01.dbf";
   set newname for datafile  7 to
 "/fs-b01-a/databases/orcl1/replication_idx-01.dbf";
   set newname for datafile  8 to
 "/fs-a01-a/databases/orcl1/rcvcat-001.dbf";
   set newname for datafile  9 to
 "/fs-b01-a/databases/orcl1/rcvcat_idx-001.dbf";
   set newname for datafile  10 to
 "/fs-d01-a/databases/orcl1/sysaux-20090310204613.dbf";
   set newname for datafile  11 to
 "/fs-d01-a/databases/orcl1/system-20090421171504.dbf";
   backup as copy reuse
   datafile  1 auxiliary format
 "/fs-b01-a/databases/orcl1/system-01.dbf"   datafile
 2 auxiliary format
 "/fs-a01-a/databases/orcl1/undo_t1-01.dbf"   datafile
 3 auxiliary format
 "/fs-a01-a/databases/orcl1/sysaux-01.dbf"   datafile
 4 auxiliary format
 "/fs-a01-a/databases/orcl1/administrator-01.dbf"   datafile
 5 auxiliary format
 "/fs-b01-a/databases/orcl1/administrator_idx-01.dbf"   datafile
 6 auxiliary format
 "/fs-a01-a/databases/orcl1/replication-01.dbf"   datafile
 7 auxiliary format
 "/fs-b01-a/databases/orcl1/replication_idx-01.dbf"   datafile
 8 auxiliary format
 "/fs-a01-a/databases/orcl1/rcvcat-001.dbf"   datafile
 9 auxiliary format
 "/fs-b01-a/databases/orcl1/rcvcat_idx-001.dbf"   datafile
 10 auxiliary format
 "/fs-d01-a/databases/orcl1/sysaux-20090310204613.dbf"   datafile
 11 auxiliary format
 "/fs-d01-a/databases/orcl1/system-20090421171504.dbf"   ;
   sql 'alter system archive log current';
}
executing Memory Script

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

renamed tempfile 1 to /fs-b01-a/databases/orcl1/temp-01.dbf in control file
renamed tempfile 2 to /fs-a01-a/databases/orcl1/dba_temp-01.dbf in control file
renamed tempfile 3 to /fs-a01-a/databases/orcl1/temp-02.dbf in control file

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

executing command: SET NEWNAME

Starting backup at 04-MAY-11
using channel ORA_DISK_1
channel ORA_DISK_1: starting datafile copy
input datafile file number=00008 name=/fs-a01-a/databases/orcl1/rcvcat-001.dbf
output file name=/fs-a01-a/databases/orcl1/rcvcat-001.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:02:05
channel ORA_DISK_1: starting datafile copy
input datafile file number=00009 name=/fs-b01-a/databases/orcl1/rcvcat_idx-001.dbf
output file name=/fs-b01-a/databases/orcl1/rcvcat_idx-001.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:02:05
channel ORA_DISK_1: starting datafile copy
input datafile file number=00010 name=/fs-d01-a/databases/orcl1/sysaux-20090310204613.dbf
output file name=/fs-d01-a/databases/orcl1/sysaux-20090310204613.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:01:15
channel ORA_DISK_1: starting datafile copy
input datafile file number=00011 name=/fs-d01-a/databases/orcl1/system-20090421171504.dbf
output file name=/fs-d01-a/databases/orcl1/system-20090421171504.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:01:05
channel ORA_DISK_1: starting datafile copy
input datafile file number=00002 name=/fs-a01-a/databases/orcl1/undo_t1-01.dbf
output file name=/fs-a01-a/databases/orcl1/undo_t1-01.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:25
channel ORA_DISK_1: starting datafile copy
input datafile file number=00004 name=/fs-a01-a/databases/orcl1/administrator-01.dbf
output file name=/fs-a01-a/databases/orcl1/administrator-01.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:25
channel ORA_DISK_1: starting datafile copy
input datafile file number=00005 name=/fs-b01-a/databases/orcl1/administrator_idx-01.dbf
output file name=/fs-b01-a/databases/orcl1/administrator_idx-01.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:25
channel ORA_DISK_1: starting datafile copy
input datafile file number=00006 name=/fs-a01-a/databases/orcl1/replication-01.dbf
output file name=/fs-a01-a/databases/orcl1/replication-01.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:25
channel ORA_DISK_1: starting datafile copy
input datafile file number=00007 name=/fs-b01-a/databases/orcl1/replication_idx-01.dbf
output file name=/fs-b01-a/databases/orcl1/replication_idx-01.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:25
channel ORA_DISK_1: starting datafile copy
input datafile file number=00001 name=/fs-b01-a/databases/orcl1/system-01.dbf
output file name=/fs-b01-a/databases/orcl1/system-01.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:15
channel ORA_DISK_1: starting datafile copy
input datafile file number=00003 name=/fs-a01-a/databases/orcl1/sysaux-01.dbf
output file name=/fs-a01-a/databases/orcl1/sysaux-01.dbf tag=TAG20110504T072447 RECID=0 STAMP=0
channel ORA_DISK_1: datafile copy complete, elapsed time: 00:00:15
Finished backup at 04-MAY-11

sql statement: alter system archive log current

contents of Memory Script:
{
   switch clone datafile all;
}
executing Memory Script

datafile 1 switched to datafile copy
input datafile copy RECID=2 STAMP=750238434 file name=/fs-b01-a/databases/orcl1/system-01.dbf
datafile 2 switched to datafile copy
input datafile copy RECID=3 STAMP=750238434 file name=/fs-a01-a/databases/orcl1/undo_t1-01.dbf
datafile 3 switched to datafile copy
input datafile copy RECID=4 STAMP=750238434 file name=/fs-a01-a/databases/orcl1/sysaux-01.dbf
datafile 4 switched to datafile copy
input datafile copy RECID=5 STAMP=750238434 file name=/fs-a01-a/databases/orcl1/administrator-01.dbf
datafile 5 switched to datafile copy
input datafile copy RECID=6 STAMP=750238434 file name=/fs-b01-a/databases/orcl1/administrator_idx-01.dbf
datafile 6 switched to datafile copy
input datafile copy RECID=7 STAMP=750238434 file name=/fs-a01-a/databases/orcl1/replication-01.dbf
datafile 7 switched to datafile copy
input datafile copy RECID=8 STAMP=750238434 file name=/fs-b01-a/databases/orcl1/replication_idx-01.dbf
datafile 8 switched to datafile copy
input datafile copy RECID=9 STAMP=750238434 file name=/fs-a01-a/databases/orcl1/rcvcat-001.dbf
datafile 9 switched to datafile copy
input datafile copy RECID=10 STAMP=750238434 file name=/fs-b01-a/databases/orcl1/rcvcat_idx-001.dbf
datafile 10 switched to datafile copy
input datafile copy RECID=11 STAMP=750238434 file name=/fs-d01-a/databases/orcl1/sysaux-20090310204613.dbf
datafile 11 switched to datafile copy
input datafile copy RECID=12 STAMP=750238434 file name=/fs-d01-a/databases/orcl1/system-20090421171504.dbf
Finished Duplicate Db at 04-MAY-11

RMAN>

This completes active duplication. Your standby is ready now.
Hope this helps !!

Avoiding “no data found” : Tips

Some time we face issue about no data found depending on selection criteria. And when this happens in PLSQL procedure we get annoying error

ORA-01403: No data found and execution stops.

One way to get around this error is to add exception block in our PLSQL procedure as shown below

EXCEPTION WHEN NO_DATA_FOUND THEN

<Take Action>

But disadvantage of using this approach is that it will not process rest of the code and control will jump to exception block and from there on continue till the end. If we get this error in the middle of FOR loop or WHILE loop, rest of the records will not get processed.

One of the way to deal with this is joining your SQL with dual.

In that case you are sure to get a null value even if the row does not exists.

Example in my case I want to find the memory value and spfile value of one of the parameter in database by querying v$parameter and v$spparameter view.

If I do a simple join as below I am going to get “no data found” if these view does not have parameter

SQL> select a.name, a.value, b.value from v$parameter b, v$spparameter a
  2  where a.name = b.name
  3  and a.name = 'shared_pool_reserved_min_alloc';

no rows selected

SQL>

But if we join with dual we can get the name of the parameter and a null value in front of that

SQL> select c.col, a.value, b.value
  2  from v$parameter b, v$spparameter a, (select 'shared_pool_reserved_min_alloc' col from dual) c
  3  where c.col = a.name (+) and c.col = b.name (+);

COL                            VALUE                VALUE
------------------------------ -------------------- --------------------
shared_pool_reserved_min_alloc

SQL>

This is easier to process.

Hope this helps !!

An insight into Oracle Index rebuild

You must have gone through my previous article on Oracle Index rebuild – 10g Vs 11g – http://avdeo.com/2011/03/17/oracle-index-rebuild-online-10g-vs-11g/

Well, there is another nice article written by a friend of mine Sumit Bhatia.

He has talked about the actions and events happening while rebuilding an index online in oracle database 11g.

Different wait events that we encounter and to make it more practical he has also build a script which gives you the exact status for your index rebuild.

You can refer to the article at http://fordba.wordpress.com/2011/04/05/online-ndx-rebuild/

You can refer to the script at http://fordba.wordpress.com/2011/03/30/ndx-progress-sql/

Just to mention, this script is by no means a complete script and enhancements can be underway. Also this script should not be used in production. If you want to use in production, you can do it at your own risk.

Hope this helps !!

Exchange Partition in Oracle

Some times we faced a need to convert our existing table from non-partition table to a partition table or vice-versa. Or moving a partition of one partition table to another partition. Or make one of the table as a partition of some partition table. Or make a partition of one of the partition table as a separate table.

To deal with this kind of situation, oracle has introduced exchange partition functionality long time a go (I guess from 8i onwards).
We will see a simple example of how to convert a simple non-partition table to a partition table.

1) Create a table, create index on table and gather stats on table and index


ORCL1>create table t as select * from dba_objects;

Table created.

ORCL1>create index t_idx on t(owner, object_name);

Index created.

ORCL1>exec dbms_stats.gather_table_stats('ADVAITD_DBA','T',cascade=>true);

PL/SQL procedure successfully completed.

ORCL1>select count(1) from t;

 COUNT(1)
----------
 14966

2) Create partition table with basic partition


ORCL1>create table pt (
 2  OWNER           VARCHAR2(30),
 3  OBJECT_NAME     VARCHAR2(128),
 4  SUBOBJECT_NAME  VARCHAR2(30),
 5  OBJECT_ID       NUMBER,
 6  DATA_OBJECT_ID  NUMBER,
 7  OBJECT_TYPE     VARCHAR2(19),
 8  CREATED         DATE,
 9  LAST_DDL_TIME   DATE,
 10  TIMESTAMP       VARCHAR2(19),
 11  STATUS          VARCHAR2(7),
 12  TEMPORARY       VARCHAR2(1),
 13  GENERATED       VARCHAR2(1),
 14  SECONDARY       VARCHAR2(1),
 15  NAMESPACE       NUMBER,
EDITION_NAME    VARCHAR2(30)
 16   17  )
 18  partition by range (CREATED)
 19  (PARTITION PT_2011 VALUES less than (MAXVALUE));

Table created.

ORCL1>select count(1) from pt;

 COUNT(1)
----------
 0

3) Exchange the partition with table


ORCL1>alter table pt exchange partition PT_2011 with table T without validation;

Table altered.

ORCL1>select count(1) from pt;

 COUNT(1)
----------
 14966

ORCL1>select count(1) from t;

 COUNT(1)
----------
 0

4) Split the partition and check

ALTER TABLE pt
SPLIT PARTITION PT_2011 AT (TO_DATE('31-DEC-2006', 'DD-MON-YYYY'))
INTO (PARTITION PT_2006,
 PARTITION PT_2011)

Analyze the table

exec dbms_stats.gather_table_stats('ADVAITD_DBA','PT',cascade=>true);

Check the number of rows in each partition


ORCL1>select TABLE_NAME, PARTITION_NAME, HIGH_VALUE, NUM_ROWS from dba_tab_partitions where TABLE_NAME = 'PT';

TABLE_NAME                     PARTITION_NAME                 HIGH_VALUE                       NUM_ROWS
------------------------------ ------------------------------ ------------------------------ ----------
PT                             PT_2006                        TO_DATE(' 2006-12-31 00:00:00'       8919
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

PT                             PT_2011                        MAXVALUE                             6047

Like that split again and check the records

ALTER TABLE pt
SPLIT PARTITION PT_2011 AT (TO_DATE('31-DEC-2007', 'DD-MON-YYYY'))
INTO (PARTITION PT_2007,
 PARTITION PT_2011)

exec dbms_stats.gather_table_stats('ADVAITD_DBA','PT',cascade=>true);

ORCL1>select TABLE_NAME, PARTITION_NAME, HIGH_VALUE, NUM_ROWS from dba_tab_partitions where TABLE_NAME = 'PT';

TABLE_NAME                     PARTITION_NAME                 HIGH_VALUE                       NUM_ROWS
------------------------------ ------------------------------ ------------------------------ ----------
PT                             PT_2006                        TO_DATE(' 2006-12-31 00:00:00'       8919
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

PT                             PT_2007                        TO_DATE(' 2007-12-31 00:00:00'        110
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

PT                             PT_2011                        MAXVALUE                             5937

Do for rest of the partitions


ALTER TABLE pt
SPLIT PARTITION PT_2011 AT (TO_DATE('31-DEC-2008', 'DD-MON-YYYY'))
INTO (PARTITION PT_2008,
 PARTITION PT_2011)

ALTER TABLE pt
SPLIT PARTITION PT_2011 AT (TO_DATE('31-DEC-2010', 'DD-MON-YYYY'))
INTO (PARTITION PT_2010,
 PARTITION PT_2011)

exec dbms_stats.gather_table_stats('ADVAITD_DBA','PT',cascade=>true);

ORCL1>select TABLE_NAME, PARTITION_NAME, HIGH_VALUE, NUM_ROWS from dba_tab_partitions where TABLE_NAME = 'PT';

TABLE_NAME                     PARTITION_NAME                 HIGH_VALUE                       NUM_ROWS
------------------------------ ------------------------------ ------------------------------ ----------
PT                             PT_2006                        TO_DATE(' 2006-12-31 00:00:00'       8919
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

PT                             PT_2007                        TO_DATE(' 2007-12-31 00:00:00'        110
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

PT                             PT_2008                        TO_DATE(' 2008-12-31 00:00:00'        250
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

TABLE_NAME                     PARTITION_NAME                 HIGH_VALUE                       NUM_ROWS
------------------------------ ------------------------------ ------------------------------ ----------

PT                             PT_2009                        TO_DATE(' 2009-12-31 00:00:00'        312
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

PT                             PT_2010                        TO_DATE(' 2010-12-31 00:00:00'        163
 , 'SYYYY-MM-DD HH24:MI:SS', 'N
 LS_CALENDAR=GREGORIA

PT                             PT_2011                        MAXVALUE                             5212

6 rows selected.

Regarding the index, we can create the similar index on partition table as well. We will create it a local index

ORCL1>create index PT_IDX on PT(owner, object_name) local;
Index created.
ORCL1>

This step can be done before as well, but doesnt matter.

Now lets verify the results


ORCL1>select count(1) from dba_objects where created < to_date('2006-12-31','YYYY-MM-DD');

 COUNT(1)
----------
 8919

ORCL1>select count(1) from dba_objects where created < to_date('2007-12-31','YYYY-MM-DD') and created > to_date('2006-12-31','YYYY-MM-DD');

 COUNT(1)
----------
 110

ORCL1>select count(1) from dba_objects where created < to_date('2008-12-31','YYYY-MM-DD') and created > to_date('2007-12-31','YYYY-MM-DD');

 COUNT(1)
----------
 250

ORCL1>select count(1) from dba_objects where created < to_date('2009-12-31','YYYY-MM-DD') and created > to_date('2008-12-31','YYYY-MM-DD');

 COUNT(1)
----------
 312

ORCL1>select count(1) from dba_objects where created < to_date('2010-12-31','YYYY-MM-DD') and created > to_date('2009-12-31','YYYY-MM-DD');

 COUNT(1)
----------
 163

ORCL1>select count(1) from dba_objects where created < to_date('2011-12-31','YYYY-MM-DD') and created > to_date('2010-12-31','YYYY-MM-DD');

 COUNT(1)
----------
 5220

RCATLTN1>

So this is how we can convert a simple non-partition table to a partition table.
Don’t forgot to gather status again after creating all partitions and indexes.

Hope this helps !!

Follow

Get every new post delivered to your Inbox.

Join 157 other followers