all about binds
DESCRIPTION
All About Binds. Thomas Kyte. It’s. All About Binds. Agenda. Performance Is it just about sharing SQL (or is this really a parsing talk in disguise) Scalability Security Do I always want to bind? What is bind variable peeking? Is it good or evil in disguise or a bit of both? - PowerPoint PPT PresentationTRANSCRIPT
All About Binds
Thomas Kyte
All About Binds
It’s
Agenda
• Performance– Is it just about sharing SQL (or is this really a parsing talk in disguise)
• Scalability • Security• Do I always want to bind? • What is bind variable peeking?
– Is it good or evil in disguise or a bit of both?
• I’m binding, but it isn’t sharing – what’s up with that?• So the developers don't bind is cursor_sharing = force/similar
appropriate system wide?• What is the real difference between cursor_sharing =
force/similar and which should we use under what circumstances?
Performance
• What is involved in a Parse– The “conventional” parse – syntax, semantic check– Optimization (can you spell C.P.U…)– Row Source Generation
• And then we can finally execute it• Conventional parse is fairly lightweight
– But it is called a “shared” pool, not “your” pool– Shared data structures have to be protected
• Optimization can be avoided• Row Source Generation can be avoided
Bind01.sql
ops$tkyte%ORA11GR2> create table t ( x int primary key );
Table created.
ops$tkyte%ORA11GR2> create table parse_stats 2 as 3 select a.name, b.value, cast( null as number ) run1, cast(null as number) run2 4 from v$statname a, v$mystat b 5 where a.statistic# = b.statistic# 6 and a.name like 'parse%';
Table created.
sqlplus
ops$tkyte%ORA11GR2> declare 2 l_cursor sys_refcursor; 3 begin 4 for i in 1 .. 10000 5 loop 6 open l_cursor for 'select /* nobind */ * from t where x = ' || i; 7 close l_cursor; 8 end loop; 9 end; 10 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:11.04
sqlplus
Bind01.sql
ops$tkyte%ORA11GR2> merge into parse_stats using ( select a.name, b.value new_val 2 from v$statname a, v$mystat b 3 where a.statistic# = b.statistic# 4 and a.name like 'parse%' ) s 5 on (parse_stats.name = s.name) when matched then update set run1 = new_val;
6 rows merged.
Elapsed: 00:00:00.15
sqlplus
Bind01.sql
ops$tkyte%ORA11GR2> declare 2 l_cursor sys_refcursor; 3 begin 4 for i in 1 .. 10000 5 loop 6 open l_cursor for 'select /* bind */ * from t where x = :x' using i; 7 close l_cursor; 8 end loop; 9 end; 10 /
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.73
sqlplus
Bind01.sql
ops$tkyte%ORA11GR2> merge into parse_stats using ( select a.name, b.value new_val 2 from v$statname a, v$mystat b 3 where a.statistic# = b.statistic# 4 and a.name like 'parse%' ) s 5 on (parse_stats.name = s.name) when matched then update set run2 = new_val;
6 rows merged.
Elapsed: 00:00:00.01
sqlplus
Bind01.sql
ops$tkyte%ORA11GR2> select name, run1, run2, 2 decode( run1, 0, null, 3 to_char(run2/run1*100,'9,999.00') || ' %' ) run2_pct_of_run1 4 from ( 5 select name, run1-value run1, run2-run1 run2 6 from parse_stats 7 ) 8 /
NAME RUN1 RUN2 RUN2_PCT_OF------------------------------ ---------- ---------- -----------parse time cpu 271 5 1.85 %parse time elapsed 278 9 3.24 %parse count (total) 10078 10018 99.40 %parse count (hard) 10014 3 .03 %parse count (failures) 0 0parse count (describe) 0 0
6 rows selected.
sqlplus
Performance
• Wonder if it might affect memory utilization?• Strange that count(*) is so low for that first query
isn’t it.• Unfortunate that sum(sharable_mem) is so high
(and remember, it really is many times that amount)
Bind02.sql
sqlplus
ops$tkyte%ORA11GR2> select count(*), to_char(sum(sharable_mem),'999,999,999') sm, 2 to_char(sum(sharable_mem)/ decode( count(*), 0, to_number(null), count(*) ), '999,999,999' ) per_stmt 3 from v$sql 4 where sql_text like 'select /* nobind */ * from t where x = %';
COUNT(*) SM PER_STMT---------- ------------ ------------ 57 781,917 13,718
ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> select count(*), to_char(sum(sharable_mem),'999,999,999') sm 2 from v$sql 3 where sql_text = 'select /* bind */ * from t where x = :x';
COUNT(*) SM---------- ------------ 1 13,856
sqlplus
ops$tkyte%ORA11GR2> declare 2 l_stmt long := 'select /*+ RULE */ * from t where x in ( 1'; 3 l_rec t%rowtype; 4 l_cursor sys_refcursor; 5 begin 6 for i in 2 .. 1000 7 loop 8 l_stmt := l_stmt || ', ' || i; 9 end loop; 10 l_stmt := l_stmt || ' ) or x in ( 1'; 11 for i in 1002 .. 2000 12 loop 13 l_stmt := l_stmt || ', ' || i; 14 end loop; 15 open l_cursor for l_stmt || ' )'; 16 fetch l_cursor into l_rec; 17 close l_cursor; 18 end; 19 /
PL/SQL procedure successfully completed.
sqlplus
ops$tkyte%ORA11GR2> select count(*), to_char(sum(sharable_mem),'999,999,999') sm 2 from v$sql 3 where sql_text like 'select /*+ RULE */ * from t where x in ( 1%';
COUNT(*) SM---------- ------------ 1 2,597,091
Scalability
• But it runs fast enough and I’ll buy more memory
• Does it really?– Run bind03.sql– Review multiuser.sql– Findings coming right up…
ops$tkyte@ORA10GR1> select 11/10000 from dual; 11/10000---------- .0011
sqlplus
ops$tkyte%ORA11GR2> create table t ( x int primary key );
Table created.
ops$tkyte%ORA11GR2> exec dbms_stats.gather_table_stats( user, 'T' );
PL/SQL procedure successfully completed.
sqlplus
ops$tkyte%ORA11GR2> create or replace procedure nobind 2 as 3 l_cursor sys_refcursor; 4 begin 5 for i in 1 .. 2000 6 loop 7 open l_cursor for 'select /* nobind */ * from t where x = ' || i; 8 close l_cursor; 9 end loop; 10 end; 11 /
Procedure created.
sqlplus
ops$tkyte%ORA11GR2> create or replace procedure bind 2 as 3 l_cursor sys_refcursor; 4 begin 5 for i in 1 .. 2000 6 loop 7 open l_cursor for 'select /* bind */ * from t where x = :x' using i; 8 close l_cursor; 9 end loop; 10 end; 11 /
Procedure created.
sqlplus
ops$tkyte%ORA11GR2> exec runstats_pkg.rs_start
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> exec nobind
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> exec runstats_pkg.rs_middle
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> exec bind
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> exec runstats_pkg.rs_stop(10000)Run1 ran in 68 hsecsRun2 ran in 8 hsecsrun 1 ran in 850% of the time
sqlplus
Name Run1 Run2 DiffLATCH.kks stats 10,831 33 -10,798LATCH.shared pool simulator 15,068 63 -15,005LATCH.row cache objects 48,470 191 -48,279STAT...session uga memory max 123,452 65,512 -57,940STAT...session uga memory 65,512 0 -65,512STAT...session pga memory max 131,072 65,536 -65,536LATCH.shared pool 87,428 327 -87,101STAT...session pga memory 65,536 -65,536 -131,072
Run1 latches total versus runs -- difference and pctRun1 Run2 Diff Pct171,431 1,150 -170,281 14,907.04%
PL/SQL procedure successfully completed.
Security
• Google sql injection• Funny thing happened during my last column
create or replace procedure set_udump (p_udump in varchar2)asbegin execute immediate 'alter system set user_dump_dest = '''||p_udump||''' scope=memory';end;/
Security
• Google sql injection• Funny thing happened during my last column
create or replace procedure set_udump (p_udump in varchar2)asbegin execute immediate 'alter system set user_dump_dest = '''||p_udump||''' scope=memory';end;/
begin set_udump('C:\ORA4\admin\ora4\udump2'' scope=memory utl_file_dir=''*'' scope=spfile user_dump_dest=''C:\ORA4\admin\ora4\udump2'); end;
Security
• Google sql injection• Funny thing happened during my last column
create or replace procedure set_udump (p_udump in varchar2)asbegin if ( p_udump NOT LIKE '%=%' ) then execute immediate 'alter system set user_dump_dest = '''||p_udump||''' scope=memory'; else raise_application_error(-20000,'Sorry, but for safety reasons this procedure does not allow "=" in the parameter value'); end if;end; inj.sql
sqlplus
ops$tkyte%ORA11GR2> create or replace procedure inj( p_date in date ) 2 as 3 l_rec all_users%rowtype; 4 c sys_refcursor; 5 l_query long; 6 begin 7 l_query := ' 8 select * 9 from all_users 10 where created = ''' ||p_date ||''''; 11 12 dbms_output.put_line( l_query ); 13 open c for l_query; 14 15 for i in 1 .. 5 16 loop 17 fetch c into l_rec; 18 exit when c%notfound; 19 dbms_output.put_line( l_rec.username || '.....' ); 20 end loop; 21 close c; 22 end; 23 /Procedure created.
sqlplus
ops$tkyte%ORA11GR2> exec inj( sysdate )
select * from all_users where created = '25-OCT-10'
PL/SQL procedure successfully completed.
sqlplus
ops$tkyte%ORA11GR2> alter session set 2 nls_date_format = 'dd-mon-yyyy"'' or ''a'' = ''a"';
Session altered.
sqlplus
ops$tkyte%ORA11GR2> exec inj( sysdate )
select * from all_users where created = '25-oct-2010' or 'a' = 'a'C.....B.....A.....FB_DEMO.....BIG_TABLE.....
PL/SQL procedure successfully completed.
Do I always want to bind?
• Always say “Never say Never”• Never say “Always”• I always say…• You do not want to
– Over Bind– Always Bind– Why….
Do I always want to bind?
• Over Binding– Compulsive disorder to eradicate all literals in SQL– Brought on by taking good advice to an illogical
extreme
– Do we need to bind those?– Might it be a bad thing to bind those?
Begin for x in ( select object_name from user_objects where object_type in ( ‘TABLE’, ‘INDEX’ )) loop …
Do I always want to bind?
• Always Binding– Data warehouse – no way.– When you run queries per second, yes.– When you run queries that take seconds, maybe,
maybe no.• Consider the frequency of the query
o 5,000 users running reports. Bindo 50 users data mining. No Bindo OLTP. Bindo End of month report. Maybe No Bind.o Common Sense, it is all about math
Bind Variable Peeking
• It is good or pure evil in disguise (neither of course)• Introduced in 9i Release 1• Makes the first hard parse of:
• Optimize as if you submitted:
• What are the assumptions then by the implementer of this feature.
Select * from emp where empno = :X;
Select * from emp where empno = 1234;
bvp01.sql
sqlplus
ops$tkyte%ORA11GR2> create table t 2 as 3 select 99 id, a.* 4 from stage a 5 where rownum <= 20000;
Table created.
ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> update t set id = 1 where rownum = 1;
1 row updated.
ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> create index t_idx on t(id);
Index created.
sqlplus
ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', 4 method_opt=>'for all indexed columns size 254', 5 estimate_percent => 100, 6 cascade=>TRUE ); 7 end; 8 /
PL/SQL procedure successfully completed.
sqlplusops$tkyte%ORA11GR2> set autotrace traceonly explainops$tkyte%ORA11GR2> select count(object_type) from t where id = 1;
Execution Plan----------------------------------------------------------Plan hash value: 1789076273
--------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 14 | 2 (0)| 00:00:01 || 1 | SORT AGGREGATE | | 1 | 14 | | || 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 14 | 2 (0)| 00:00:01 ||* 3 | INDEX RANGE SCAN | T_IDX | 1 | | 1 (0)| 00:00:01 |--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
3 - access("ID"=1)
ops$tkyte%ORA11GR2> set autotrace off
sqlplusops$tkyte%ORA11GR2> set autotrace traceonly explainops$tkyte%ORA11GR2> select count(object_type) from t where id = 99;
Execution Plan----------------------------------------------------------Plan hash value: 2966233522
---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 14 | 82 (0)| 00:00:01 || 1 | SORT AGGREGATE | | 1 | 14 | | ||* 2 | TABLE ACCESS FULL| T | 19999 | 273K| 82 (0)| 00:00:01 |---------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
2 - filter("ID"=99)
ops$tkyte%ORA11GR2> set autotrace off
sqlplusops$tkyte%ORA11GR2> variable n numberops$tkyte%ORA11GR2> exec :n := 1PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> explain plan for 2 select count(object_type) from t n_is_1_first where id = :n;Explained.
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT--------------------Plan hash value: 2966233522---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 14 | 82 (0)| 00:00:01 || 1 | SORT AGGREGATE | | 1 | 14 | | ||* 2 | TABLE ACCESS FULL| T | 10000 | 136K| 82 (0)| 00:00:01 |---------------------------------------------------------------------------
Predicate Information (identified by operation id):--------------------------------------------------- 2 - filter("ID"=TO_NUMBER(:N))
14 rows selected.
sqlplus
ops$tkyte%ORA11GR2> alter session set events '10046 trace name context forever, level 12';
Session altered.
ops$tkyte%ORA11GR2> select count(object_type) from t n_is_1_first where id = :n;
COUNT(OBJECT_TYPE)------------------ 1
sqlplus
ops$tkyte%ORA11GR2> exec :n := 99
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> select count(object_type) from t n_is_1_first where id = :n;
COUNT(OBJECT_TYPE)------------------ 19999
sqlplus
ops$tkyte%ORA11GR2> select count(object_type) from t n_is_99_first where id = :n;
COUNT(OBJECT_TYPE)------------------ 19999
sqlplus
ops$tkyte%ORA11GR2> select count(object_type) from t n_is_99_first where id = :n;
COUNT(OBJECT_TYPE)------------------ 1
sqlplusselect count(object_type)from t n_is_1_first where id = :n
call count cpu elapsed disk query current rows------- ------ -------- ---------- ---------- ---------- ---------- ----------Parse 1 0.00 0.00 0 0 0 0Execute 1 0.00 0.07 0 0 0 0Fetch 2 0.00 0.02 0 3 0 1------- ------ -------- ---------- ---------- ---------- ---------- ----------total 4 0.00 0.09 0 3 0 1
Misses in library cache during parse: 1Misses in library cache during execute: 1Optimizer mode: ALL_ROWSParsing user id: 211 (OPS$TKYTE)
Rows Row Source Operation------- --------------------------------------------------- 1 SORT AGGREGATE (cr=3 pr=0 pw=0 time=0 us) 1 TABLE ACCESS BY INDEX ROWID T (cr=3 pr=0 pw=0 time=0 us cost=2 size=14 card=1) 1 INDEX RANGE SCAN T_IDX (cr=2 pr=0 pw=0 time=0 us cost=1 size=0 card=1)(object id 88040)
Rows Execution Plan------- --------------------------------------------------- 0 SELECT STATEMENT MODE: ALL_ROWS 1 SORT (AGGREGATE) 1 TABLE ACCESS MODE: ANALYZED (FULL) OF 'T' (TABLE)
sqlplusselect count(object_type) from t n_is_1_first where id = :n
call count cpu elapsed disk query current rows------- ------ -------- ---------- ---------- ---------- ---------- ----------Parse 1 0.00 0.00 0 0 0 0Execute 1 0.00 0.00 0 0 0 0Fetch 2 0.00 0.01 0 322 0 1------- ------ -------- ---------- ---------- ---------- ---------- ----------total 4 0.00 0.01 0 322 0 1
Misses in library cache during parse: 0Optimizer mode: ALL_ROWSParsing user id: 211 (OPS$TKYTE)
Rows Row Source Operation------- --------------------------------------------------- 1 SORT AGGREGATE (cr=322 pr=0 pw=0 time=0 us) 19999 TABLE ACCESS BY INDEX ROWID T (cr=322 pr=0 pw=0 time=76043 us cost=2 size=14 card=1) 19999 INDEX RANGE SCAN T_IDX (cr=41 pr=0 pw=0 time=25984 us cost=1 size=0 card=1)(object id 88040)
Rows Execution Plan------- --------------------------------------------------- 0 SELECT STATEMENT MODE: ALL_ROWS 1 SORT (AGGREGATE) 19999 TABLE ACCESS MODE: ANALYZED (FULL) OF 'T' (TABLE)
sqlplusselect count(object_type)from t n_is_99_first where id = :n
call count cpu elapsed disk query current rows------- ------ -------- ---------- ---------- ---------- ---------- ----------Parse 1 0.00 0.00 0 0 0 0Execute 1 0.00 0.00 0 0 0 0Fetch 2 0.00 0.00 0 285 0 1------- ------ -------- ---------- ---------- ---------- ---------- ----------total 4 0.00 0.00 0 285 0 1
Misses in library cache during parse: 1Misses in library cache during execute: 1Optimizer mode: ALL_ROWSParsing user id: 211 (OPS$TKYTE)
Rows Row Source Operation------- --------------------------------------------------- 1 SORT AGGREGATE (cr=285 pr=0 pw=0 time=0 us) 19999 TABLE ACCESS FULL T (cr=285 pr=0 pw=0 time=24838 us cost=82 size=279986 card=19999)
Rows Execution Plan------- --------------------------------------------------- 0 SELECT STATEMENT MODE: ALL_ROWS 1 SORT (AGGREGATE) 19999 TABLE ACCESS MODE: ANALYZED (FULL) OF 'T' (TABLE)
sqlplusselect count(object_type)from t n_is_99_first where id = :n
call count cpu elapsed disk query current rows------- ------ -------- ---------- ---------- ---------- ---------- ----------Parse 1 0.00 0.00 0 0 0 0Execute 1 0.00 0.00 0 0 0 0Fetch 2 0.00 0.00 0 285 0 1------- ------ -------- ---------- ---------- ---------- ---------- ----------total 4 0.00 0.00 0 285 0 1
Misses in library cache during parse: 0Optimizer mode: ALL_ROWSParsing user id: 211 (OPS$TKYTE)
Rows Row Source Operation------- --------------------------------------------------- 1 SORT AGGREGATE (cr=285 pr=0 pw=0 time=0 us) 1 TABLE ACCESS FULL T (cr=285 pr=0 pw=0 time=0 us cost=82 size=279986 card=19999)
Rows Execution Plan------- --------------------------------------------------- 0 SELECT STATEMENT MODE: ALL_ROWS 1 SORT (AGGREGATE) 1 TABLE ACCESS MODE: ANALYZED (FULL) OF 'T' (TABLE)
Bind Variable Peeking
• Adaptive Cursor Sharing– New in 11gR1– Will help solve most bind variable peeking issues– Works currently with equality and range operations (but
not “like” predicates)
bvp03.sql
sqlplus
ops$tkyte%ORA11GR2> create table t 2 as 3 select 99 id, a.* 4 from stage a 5 where rownum <= 20000;
Table created.
ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> update t set id = 1 where rownum = 1;
1 row updated.
ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> create index t_idx on t(id);
Index created.
sqlplus
ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', 4 method_opt=>'for all indexed columns size 254', 5 estimate_percent => 100, 6 cascade=>TRUE ); 7 end; 8 /
PL/SQL procedure successfully completed.
sqlplusops$tkyte%ORA11GR2> exec :n := 1;PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> select /*BVP03*/ count(subobject_name) from t where id = :n;
COUNT(SUBOBJECT_NAME)--------------------- 0
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT-----------------------SQL_ID 4un62u0uttg6u, child number 0-------------------------------------select /*BVP03*/ count(subobject_name) from t where id = :n
Plan hash value: 1789076273--------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 2 (100)| || 1 | SORT AGGREGATE | | 1 | 20 | | || 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 20 | 2 (0)| 00:00:01 ||* 3 | INDEX RANGE SCAN | T_IDX | 1 | | 1 (0)| 00:00:01 |--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):--------------------------------------------------- 3 - access("ID"=:N)
20 rows selected.
sqlplus
ops$tkyte%ORA11GR2> select child_number, executions, buffer_gets, 2 is_bind_sensitive, is_bind_aware 3 from v$sql 4 where sql_text like 'select /*BVP03*/ %';
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE------------ ---------- ----------- ------------------ --------------- 0 1 3 Y N
sqlplusops$tkyte%ORA11GR2> exec :n := 99;PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> select /*BVP03*/ count(subobject_name) from t where id = :n;
COUNT(SUBOBJECT_NAME)--------------------- 118
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT-----------------------------------------SQL_ID 4un62u0uttg6u, child number 0-------------------------------------select /*BVP03*/ count(subobject_name) from t where id = :n
Plan hash value: 1789076273--------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 2 (100)| || 1 | SORT AGGREGATE | | 1 | 20 | | || 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 20 | 2 (0)| 00:00:01 ||* 3 | INDEX RANGE SCAN | T_IDX | 1 | | 1 (0)| 00:00:01 |--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):--------------------------------------------------- 3 - access("ID"=:N)
20 rows selected.
sqlplus
ops$tkyte%ORA11GR2> select child_number, executions, buffer_gets, 2 is_bind_sensitive, is_bind_aware 3 from v$sql 4 where sql_text like 'select /*BVP03*/ %';
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE------------ ---------- ----------- ------------------ --------------- 0 2 325 Y N
sqlplusops$tkyte%ORA11GR2> select /*BVP03*/ count(subobject_name) from t where id = :n;
COUNT(SUBOBJECT_NAME)--------------------- 118
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT-------------------------SQL_ID 4un62u0uttg6u, child number 1-------------------------------------select /*BVP03*/ count(subobject_name) from t where id = :n
Plan hash value: 2966233522---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 82 (100)| || 1 | SORT AGGREGATE | | 1 | 20 | | ||* 2 | TABLE ACCESS FULL| T | 19999 | 390K| 82 (0)| 00:00:01 |---------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
2 - filter("ID"=:N)
19 rows selected.
sqlplus
ops$tkyte%ORA11GR2> select child_number, executions, buffer_gets, 2 is_bind_sensitive, is_bind_aware 3 from v$sql 4 where sql_text like 'select /*BVP03*/ %';
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE------------ ---------- ----------- ------------------ --------------- 0 2 325 Y N 1 1 285 Y Y
sqlplusops$tkyte%ORA11GR2> exec :n := 1PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> select /*BVP03*/ count(subobject_name) from t where id = :n;
COUNT(SUBOBJECT_NAME)--------------------- 0
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT---------------------------SQL_ID 4un62u0uttg6u, child number 2-------------------------------------select /*BVP03*/ count(subobject_name) from t where id = :n
Plan hash value: 1789076273--------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |--------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 2 (100)| || 1 | SORT AGGREGATE | | 1 | 20 | | || 2 | TABLE ACCESS BY INDEX ROWID| T | 1 | 20 | 2 (0)| 00:00:01 ||* 3 | INDEX RANGE SCAN | T_IDX | 1 | | 1 (0)| 00:00:01 |--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):--------------------------------------------------- 3 - access("ID"=:N)
20 rows selected.
sqlplus
ops$tkyte%ORA11GR2> select child_number, executions, buffer_gets, 2 is_bind_sensitive, is_bind_aware 3 from v$sql 4 where sql_text like 'select /*BVP03*/ %';
CHILD_NUMBER EXECUTIONS BUFFER_GETS IS_BIND_SENSITIVE IS_BIND_AWARE------------ ---------- ----------- ------------------ --------------- 0 2 325 Y N 1 1 285 Y Y 2 1 3 Y Y
Bind Variable Peeking over time
• 8i and before– no peeking
• 9i, 10g – Peeking– First plan “wins”
• 11g and above– Adaptive Cursor Sharing
Bind Variable Peeking• What can you do when those assumptions don’t hold true for
you in a specific case? – 11gR1 and above!!– Don’t bind that query, that is a possibility.
• Do the math… – Don’t use histograms
• Get the “general plan”• Consistent Plan, but typically not the “best” plan for all
– Use your domain knowledge• Input dates within the last month – use this query, else use that query• Codes less than 50 – use this query, else use that query• Status values of ‘A’, ‘M’ and ‘N’ …. Else….
– Cursor_sharing = similar– You can disable it – but that is like “don’t use histograms” in a
system that uses binds.
I’m binding, but it isn’t sharing
• Many things can do that– Any environmental variables that affect the optimizer– Or security (this is why PLSQL rules)– Bind Type mismatch– Language– PLSQL compiler switches
• For example, lets tune with SQL_TRACE=TRUE• And Look deeper at “bind mismatches”• Desc v$sql_shared_cursor
tune.sqlBindmis.sql
sqlplus
ops$tkyte%ORA11GR2> alter session set "_optimizer_extended_cursor_sharing_rel"=none;
Session altered.
ops$tkyte%ORA11GR2> create table t 2 as 3 select 99 id, a.* 4 from stage a 5 where rownum <= 20000;
Table created.
ops$tkyte%ORA11GR2> update t set id = 1 where rownum = 1;
1 row updated.
ops$tkyte%ORA11GR2> create index t_idx on t(id);
Index created.
sqlplus
ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'T', 4 method_opt => 'for all indexed columns size 254', 5 estimate_percent => 100, 6 cascade=> true); 7 end; 8 /
PL/SQL procedure successfully completed.
sqlplusops$tkyte%ORA11GR2> variable n numberops$tkyte%ORA11GR2> exec :n := 99
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> set autotrace traceonly statisticsops$tkyte%ORA11GR2> select * from t where id = :n;
19999 rows selected.
Statistics---------------------------------------------------------- 0 recursive calls 0 db block gets 1601 consistent gets 0 physical reads 0 redo size 928023 bytes sent via SQL*Net to client 15082 bytes received via SQL*Net from client 1335 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 19999 rows processed
ops$tkyte%ORA11GR2> set autotrace off
sqlplus
ops$tkyte%ORA11GR2> exec :n := 1
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> set autotrace traceonly statisticsops$tkyte%ORA11GR2> select * from t where id = :n;
Statistics---------------------------------------------------------- 0 recursive calls 0 db block gets 286 consistent gets 0 physical reads 0 redo size 1448 bytes sent via SQL*Net to client 419 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
ops$tkyte%ORA11GR2> set autotrace off
sqlplus
ops$tkyte%ORA11GR2> alter session set events '10046 trace name context forever, level 12';
Session altered.
ops$tkyte%ORA11GR2> select * from t where id = :n;
Statistics---------------------------------------------------------- 1 recursive calls 0 db block gets 4 consistent gets 0 physical reads 0 redo size 1448 bytes sent via SQL*Net to client 419 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
sqlplus
ops$tkyte%ORA11GR2> alter session set events '10046 trace name context off';
Session altered.
ops$tkyte%ORA11GR2> select * from t where id = :n;
Statistics---------------------------------------------------------- 0 recursive calls 0 db block gets 286 consistent gets 0 physical reads 0 redo size 1448 bytes sent via SQL*Net to client 419 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
sqlplus
ops$tkyte%ORA11GR2> alter session set events '10046 trace name context forever, level 12';
Session altered.
ops$tkyte%ORA11GR2> select * from t where id = :n;
Statistics---------------------------------------------------------- 0 recursive calls 0 db block gets 4 consistent gets 0 physical reads 0 redo size 1448 bytes sent via SQL*Net to client 419 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
sqlplus
ops$tkyte%ORA11GR2> alter session set events '10046 trace name context off';
Session altered.
ops$tkyte%ORA11GR2> select * from t where id = :n;
Statistics---------------------------------------------------------- 0 recursive calls 0 db block gets 286 consistent gets 0 physical reads 0 redo size 1448 bytes sent via SQL*Net to client 419 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed
sqlplusops$tkyte%ORA11GR2> select parsing_user_id puid, parsing_schema_id psid, is_bind_aware, is_bind_sensitive, 2 address, child_address 3 from v$sql 4 where sql_text = 'select * from t where id = :n';
PUID PSID IS_BIND_AWARE IS_BIND_SENSITIVE ADDRESS CHILD_AD---------- ---------- --------------- ------------------ -------- -------- 211 211 N N 32448F98 2D55B0B0 211 211 N N 32448F98 3245FF8C
sqlplus
ops$tkyte%ORA11GR2> alter session set "_optimizer_extended_cursor_sharing_rel"=simple;
Session altered.
sqlplus
ops$tkyte%ORA11GR2> create table t ( x varchar2(2000) );
Table created.
sqlplus
ops$tkyte%ORA11GR2> declare 2 a varchar2(1) := 'x'; 3 b varchar2(100) := rpad('x',100,'x'); 4 c varchar2(500) := rpad('x',500,'x'); 5 d varchar2(1000) := rpad('x',1000,'x'); 6 begin 7 insert into t values(a); 8 insert into t values(b); 9 insert into t values(c); 10 insert into t values(d); 11 end; 12 /
PL/SQL procedure successfully completed.
sqlplus
ops$tkyte%ORA11GR2> select parsing_user_id puid, parsing_schema_id psid, 2 sql_text, address, child_address 3 from v$sql 4 where sql_text like 'INSERT%INTO%T%VALUES(%:B1%)' 5 /
PUID PSID SQL_TEXT ADDRESS CHILD_AD---------- ---------- ------------------------------ -------- -------- 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2D77BDF8 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 325BFA18 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2C284A44
sqlplusops$tkyte%ORA11GR2> select * 2 from v$sql_shared_cursor 3 where address = '&ADDR' 4 /old 3: where address = '&ADDR'new 3: where address = '325F10B8'
SQL_ID ADDRESS CHILD_AD CHILD_NUMBER U S O O S L F E B P I S T A B D L T B I I R L I O E M U T N F A I T D L D B------------- -------- -------- ------------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -P C S C P T M B M R O P M F L P L A F L R L H P B- - - - - - - - - - - - - - - - - - - - - - - - -9bay73nakuyw9 325F10B8 2D77BDF8 0 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N NN N N N N N N N N N N N N N N N N N N N N N N N N
9bay73nakuyw9 325F10B8 325BFA18 1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N NN N N N N N N N N N N N N N N N N N N N N N N N Y
9bay73nakuyw9 325F10B8 2C284A44 2 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N N NN N N N N N N N N N N N N N N N N N N N N N N N Y
sqlplus
ops$tkyte%ORA11GR2> select bind_mismatch, BIND_LENGTH_UPGRADEABLE 2 from v$sql_shared_cursor 3 where address = '&ADDR' 4 /old 3: where address = '&ADDR'new 3: where address = '325F10B8'
B B- -N NN YN Y
sqlplus
ops$tkyte%ORA11GR2> declare 2 a varchar2(1) := 'x'; 3 b varchar2(10) := rpad('x',10,'x'); 4 c varchar2(20) := rpad('x',20,'x'); 5 d varchar2(30) := rpad('x',30,'x'); 6 begin 7 insert into t xxx values(a); 8 insert into t xxx values(b); 9 insert into t xxx values(c); 10 insert into t xxx values(d); 11 end; 12 /
PL/SQL procedure successfully completed.
sqlplus
ops$tkyte%ORA11GR2> select parsing_user_id puid, parsing_schema_id psid, 2 sql_text, address, child_address 3 from v$sql 4 where sql_text like 'INSERT%INTO%T%VALUES(%:B1%)' 5 /
PUID PSID SQL_TEXT ADDRESS CHILD_AD---------- ---------- ------------------------------ -------- -------- 211 211 INSERT INTO T XXX VALUES(:B1 ) 2D4C7DEC 2C33D5A4 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2D77BDF8 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 325BFA18 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2C284A44
sqlplus
ops$tkyte%ORA11GR2> declare 2 a varchar2(1) := 'x'; 3 b varchar2(10) := rpad('x',10,'x'); 4 c varchar2(32) := rpad('x',32,'x'); 5 d varchar2(33) := rpad('x',33,'x'); 6 begin 7 insert into t yyy values(a); 8 insert into t yyy values(b); 9 insert into t yyy values(c); 10 insert into t yyy values(d); 11 end; 12 /
PL/SQL procedure successfully completed.
sqlplus
ops$tkyte%ORA11GR2> select parsing_user_id puid, parsing_schema_id psid, 2 sql_text, address, child_address 3 from v$sql 4 where sql_text like 'INSERT%INTO%T%VALUES(%:B1%)' 5 /
PUID PSID SQL_TEXT ADDRESS CHILD_AD---------- ---------- ------------------------------ -------- -------- 211 211 INSERT INTO T YYY VALUES(:B1 ) 2D55EF40 2C1E7E8C 211 211 INSERT INTO T YYY VALUES(:B1 ) 2D55EF40 32474080 211 211 INSERT INTO T XXX VALUES(:B1 ) 2D4C7DEC 2C33D5A4 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2D77BDF8 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 325BFA18 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2C284A44
6 rows selected.
sqlplus
ops$tkyte%ORA11GR2> declare 2 a varchar2(1) := 'x'; 3 b varchar2(100) := rpad('x',100,'x'); 4 c varchar2(500) := rpad('x',500,'x'); 5 d varchar2(1000) := rpad('x',1000,'x'); 6 begin 7 insert into t zzz values(d); 8 insert into t zzz values(c); 9 insert into t zzz values(b); 10 insert into t zzz values(a); 11 end; 12 /
PL/SQL procedure successfully completed.
sqlplus
ops$tkyte%ORA11GR2> select parsing_user_id puid, parsing_schema_id psid, 2 sql_text, address, child_address 3 from v$sql 4 where sql_text like 'INSERT%INTO%T%VALUES(%:B1%)' 5 /
PUID PSID SQL_TEXT ADDRESS CHILD_AD---------- ---------- ------------------------------ -------- -------- 211 211 INSERT INTO T YYY VALUES(:B1 ) 2D55EF40 2C1E7E8C 211 211 INSERT INTO T YYY VALUES(:B1 ) 2D55EF40 32474080 211 211 INSERT INTO T XXX VALUES(:B1 ) 2D4C7DEC 2C33D5A4 211 211 INSERT INTO T ZZZ VALUES(:B1 ) 2D5ECF98 2D52AFD0 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2D77BDF8 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 325BFA18 211 211 INSERT INTO T VALUES(:B1 ) 325F10B8 2C284A44
7 rows selected.
Cursor Sharing
• So the developers don't bind is cursor_sharing = force/similar appropriate system wide?
No
Cursor Sharing
• Negatively Impacts Well Written Applications– They run slower even if plans do not change– We just did bind variable peeking, so we know about
• Over binding (this is over binding defined)• Always binding (this is always binding defined)
– Possible plan changes• Optimizer has less information, doesn’t have the facts
– Behavior Changes• Don’t know column widths anymore• Don’t know scale/precision anymore
cs01.sql
sqlplus
ops$tkyte%ORA11GR2> alter session set cursor_sharing=exact;
Session altered.
ops$tkyte%ORA11GR2> create table t 2 as 3 select * 4 from all_users 5 where rownum = 1;
Table created.
sqlplus
ops$tkyte%ORA11GR2> alter session set cursor_sharing=exact;
Session altered.
ops$tkyte%ORA11GR2> select substr(username,1,10) uname, 2 to_char(user_id,'999,999') u_id, 3 to_char(created,'Dy Mon DD, YYYY' ) created 4 from t cs_exact;
UNAME U_ID CREATED---------- -------- ----------------SYS 0 Thu Aug 13, 2009
sqlplus
ops$tkyte%ORA11GR2> alter session set cursor_sharing=force;
Session altered.
ops$tkyte%ORA11GR2> select substr(username,1,10) uname, 2 to_char(user_id,'999,999') u_id, 3 to_char(created,'Dy Mon DD, YYYY' ) created 4 from t cs_force;
UNAME------------------------------U_ID-------------------------------------------------------------------------------------------------------------------------CREATED---------------------------------------------------------------------------SYS 0Thu Aug 13, 2009
sqlplusops$tkyte%ORA11GR2> alter session set cursor_sharing=exact;
Session altered.
ops$tkyte%ORA11GR2> column sql_text format a80ops$tkyte%ORA11GR2> select sql_text 2 from v$sql 3 where sql_text like 'select substr(username%';
SQL_TEXT--------------------------------------------------------------------------------select substr(username,1,10) uname, to_char(user_id,'999,999') u_id, to_char(created,'Dy Mon DD, YYYY' ) created from t cs_exact
select substr(username,:"SYS_B_0",:"SYS_B_1") uname, to_char(user_id,:"SYS_B_2") u_id, to_char(created,:"SYS_B_3" ) created from t cs_force
Force/Similar
• Force is just that– All literals, without any regard to anything, will be
replaced with binds– 10g and before - There will be probably one plan
generated (all things considered the same! Remember v$sql_shared_cursor)
• Consider the bind variable peeking implicationso Cold start, first query is id=99o Cold start, first query is id=1o Bouncing the database is my tuning tool?
– But, things change… 11gR1 and up
Force/Similar
• Similar– When replacing the bind with a literal (reversed purposely)
could change the plan… – Multiple child cursors will be developed– Each can have it’s own unique plan– Optimization will use the “best” plan– Is this better than force?
• Depends• More child cursors• Longer code path
– But is does solve a little more of the problem.
similar.sql
sqlplus
ops$tkyte%ORA11GR2> create table emp as select * from scott.emp;Table created.
ops$tkyte%ORA11GR2> update emp set deptno = 99;
14 rows updated.
ops$tkyte%ORA11GR2> begin 2 for i in 1 .. 11 3 loop 4 insert /*+ append */ into emp select * from emp; 5 commit; 6 end loop; 7 end; 8 /
PL/SQL procedure successfully completed.
ops$tkyte%ORA11GR2> select count(*) from emp;
COUNT(*)---------- 28672
sqlplus
ops$tkyte%ORA11GR2> update emp set deptno = 1 where rownum = 1;
1 row updated.
ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> create index dept_idx on emp(deptno);
Index created.
ops$tkyte%ORA11GR2> ops$tkyte%ORA11GR2> begin 2 dbms_stats.gather_table_stats 3 ( user, 'EMP', 4 method_opt=>'for all indexed columns', 5 estimate_percent => 100, 6 cascade=>true ); 7 end; 8 /
PL/SQL procedure successfully completed.
sqlplusops$tkyte%ORA11GR2> select count(empno) from emp where deptno = 99;
Execution Plan----------------------------------------------------------Plan hash value: 2083865914
---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 16 | 53 (0)| 00:00:01 || 1 | SORT AGGREGATE | | 1 | 16 | | ||* 2 | TABLE ACCESS FULL| EMP | 28671 | 447K| 53 (0)| 00:00:01 |---------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
2 - filter("DEPTNO"=99)
sqlplusops$tkyte%ORA11GR2> select count(empno) from emp where deptno = 1;
Execution Plan----------------------------------------------------------Plan hash value: 237848182
-----------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 16 | 2 (0)| 00:00:01 || 1 | SORT AGGREGATE | | 1 | 16 | | || 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 16 | 2 (0)| 00:00:01 ||* 3 | INDEX RANGE SCAN | DEPT_IDX | 1 | | 1 (0)| 00:00:01 |-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
3 - access("DEPTNO"=1)
sqlplus
ops$tkyte%ORA11GR2> alter session set events '10046 trace name context forever, level 12';
Session altered.
ops$tkyte%ORA11GR2> alter session set cursor_sharing=similar;
Session altered.
sqlplusops$tkyte%ORA11GR2> select count(empno) from emp CSS where deptno = 1;COUNT(EMPNO)------------ 1
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT-------------------------------------------------------------------------------------------------------------------------SQL_ID 93pjrvfdz49m5, child number 0-------------------------------------select count(empno) from emp CSS where deptno = :"SYS_B_0"
Plan hash value: 237848182
-----------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |-----------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 2 (100)| || 1 | SORT AGGREGATE | | 1 | 16 | | || 2 | TABLE ACCESS BY INDEX ROWID| EMP | 1 | 16 | 2 (0)| 00:00:01 ||* 3 | INDEX RANGE SCAN | DEPT_IDX | 1 | | 1 (0)| 00:00:01 |-----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
3 - access("DEPTNO"=:SYS_B_0)
20 rows selected.
sqlplusops$tkyte%ORA11GR2> select count(empno) from emp CSS where deptno = 99;COUNT(EMPNO)------------ 28671
ops$tkyte%ORA11GR2> select * from table(dbms_xplan.display_cursor);
PLAN_TABLE_OUTPUT-------------------------------------------------------------------------------------------------------------------------SQL_ID 93pjrvfdz49m5, child number 1-------------------------------------select count(empno) from emp CSS where deptno = :"SYS_B_0"
Plan hash value: 2083865914
---------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |---------------------------------------------------------------------------| 0 | SELECT STATEMENT | | | | 53 (100)| || 1 | SORT AGGREGATE | | 1 | 16 | | ||* 2 | TABLE ACCESS FULL| EMP | 28671 | 447K| 53 (0)| 00:00:01 |---------------------------------------------------------------------------
Predicate Information (identified by operation id):---------------------------------------------------
2 - filter("DEPTNO"=:SYS_B_0)
19 rows selected.
sqlplus
ops$tkyte%ORA11GR2> alter session set cursor_sharing=exact;
Session altered.
ops$tkyte%ORA11GR2> select sql_text, is_bind_aware, is_bind_sensitive from v$sql 2 where sql_text like 'select count(empno) from emp CSS where deptno =%';
SQL_TEXT IS_BIND_AWARE IS_BIND_SENSITIVE---------------------------------------- --------------- ------------------select count(empno) from emp CSS where d N Yeptno = :"SYS_B_0"
select count(empno) from emp CSS where d N Yeptno = :"SYS_B_0"
Force/Similar• In Short, in 10g and before just say
NoTo setting at the system level, this is an application level bug “workaround until we get it fixed for real” tool
Questions