Wednesday, December 26, 2007

lobs vs strings

Recently one of the emails I read at a client site (wherein the lead had recommended his team to use dbms_lob.substr in the sqls) probed me do this research and as always learning new stuff in oracle is never ending. :)










SqL>Create table test_clob (c1 clob,c2 number);

Table created.


SqL>ed
Wrote file afiedt.buf

1* Insert into test_clob values (Rpad('Test',50000,'*'),null)
SqL>/

1 row created.

SqL>ed
Wrote file afiedt.buf

1* Insert into test_clob values (Rpad('Test2',100000,'*'),null)
SqL>/

1 row created.

SqL>update test_clob set c2=dbms_lob.getlength(c1);

2 rows updated.

SqL>commit;

Commit complete.

SqL>set long 30
SqL>col c1 format a30 trunc

SqL>select c1,c2 from test_clob;

C1 C2
------------------------------ ----------
Test************************** 4000
Test2************************* 4000

SqL>ed
Wrote file afiedt.buf

1* select c1,c2,length(c1) from test_clob
SqL>/

C1 C2 LENGTH(C1)
------------------------------ ---------- ----------
Test************************** 4000 4000
Test2************************* 4000 4000


SqL>ed
Wrote file afiedt.buf

1 declare
2 lv_tmp clob;
3 lv_tmp1 varchar2(4000);
4 begin
5 lv_tmp1:= Rpad('Test3',4000,'*');
6 for i in 1..10 loop
7 lv_tmp:= lv_tmp||lv_tmp1;
8 end loop;
9 Insert into test_clob(c1) values (lv_tmp);
10 commit;
11* end;
12 /

PL/SQL procedure successfully completed.

SqL>update test_clob set c2=dbms_lob.getlength(c1) where c2 is null;

1 row updated.

SqL>commit;

Commit complete.

SqL>select c1,c2,length(c1) from test_clob;

C1 C2 LENGTH(C1)
------------------------------ ---------- ----------
Test************************** 4000 4000
Test2************************* 4000 4000
Test3************************* 40000 40000





first interesting thing I noticed was I couldnt use rpad function to generate a string more than 4000 chrs..Its strange that oracle silently ignores the request to create longer strings (request was made to make it as long as 100000 chrs long) but it extended only upto 4000.

Well, that takes me to using PLSQL to insert clob data.

now comes the interesting part..lets say we need to fetch only part of the data from clob..what would you use? dbms_lob.substr function or regular substr function?










SqL>ed
Wrote file afiedt.buf

1 With v1 as (select &length len,&starting_chr startpos from dual)
2 select
3 length(substr(c1,startpos,len)) substr_len
4 , length(dbms_lob.substr(c1,len,startpos)) substr_lob
5* from test_clob,v1 where c2=40000
SqL>/
Enter value for length: 4000
Enter value for starting_chr: 1
old 1: With v1 as (select &length len,&starting_chr startpos from dual)
new 1: With v1 as (select 4000 len,1 startpos from dual)

SUBSTR_LEN SUBSTR_LOB
---------- ----------
4000 4000

SqL>/
Enter value for length: 4001
Enter value for starting_chr: 1
old 1: With v1 as (select &length len,&starting_chr startpos from dual)
new 1: With v1 as (select 4001 len,1 startpos from dual)
, length(dbms_lob.substr(c1,len,startpos)) substr_lob
*
ERROR at line 4:
ORA-06502: PL/SQL: numeric or value error: character string buffer too small
ORA-06512: at line 1

SqL>ed
Wrote file afiedt.buf

1 With v1 as (select &length len,&starting_chr startpos from dual)
2 select
3 length(substr(c1,startpos,len)) substr_len
4 , dbms_lob.getlength(substr(c1,startpos,len)) substr_lob
5 -- , length(dbms_lob.substr(c1,len,startpos)) substr_lob
6* from test_clob,v1 where c2=40000
SqL>/
Enter value for length: 5000
Enter value for starting_chr: 1
old 1: With v1 as (select &length len,&starting_chr startpos from dual)
new 1: With v1 as (select 5000 len,1 startpos from dual)

SUBSTR_LEN SUBSTR_LOB
---------- ----------
5000 5000





what does that tell? dbms_lob.substr cannot return more than 4K characters ..whereas substr can..

It all comes back to datatypes and why understanding them is necessary..I checked the manual and its clearly stated substr can take a clob as its input and return "clob" ..whereas dbms_lob if it accepts clob, returns varchar2 (hence limited to 4000 characters).



Wednesday, November 28, 2007

Interesting SQL problem

Came across this interesting sql question on oracle-l list while I was searching for something else.

The poster's original question is as below..


create table t(id number);
insert into t values(1);
insert into t values(2);
commit;

I want to query this with an Id set. All values in the set should be
there to return me any row.
e.g.
select * from t where id in (1,2); return 1 and 2

If am serching for 1,2,3 if any one value is missing I should not get any data.
e.g.
select * from t where id in (1,2,3) should not return any row.
How to rewrite the above query with (1,2,3) that should not return me any row.



Here is my solution for that...


ccD>l
1 With inputstr as
2 ( select '&inp' elements from dual),
3 setdata
4 as
5 (
6 select
7 trim( substr (txt,
8 instr (txt, ',', 1, level ) + 1,
9 instr (txt, ',', 1, level+1)
10 - instr (txt, ',', 1, level) -1 ) )
11 as token
12 from (select ','||elements||',' txt
13 from inputstr ) t,inputstr i
14 connect by level <=
15 length(i.elements)-length(replace(i.elements,',',''))+1
16 )
17 select token from
18 (
19 select
20 to_number(token) token,
21 nvl2(t.id,1,0) present,
22 min(nvl2(t.id,1,0)) over() min_over_report
23 from setdata s, t
24 where s.token=t.id(+)
25 )
26* where min_over_report=1



Testing
--------


ccD>select * from t; -- this is what the table contains
ID
----------
1
2
ccD> get the above sql into buffer
ccD>/
Enter value for inp: 1,2 -- input is 1,2 and it retrives two rows
TOKEN
----------
1
2
ccD>/
Enter value for inp: 1,2,3 -- 1,2,3 retrives no rows because 3 is not present
no rows selected
ccD>/
Enter value for inp: 1,2,3,4 -- same with 1,2,3,4
no rows selected
ccD>/
Enter value for inp: 1 -- input 1 retrives one row
TOKEN
----------
1



Monday, November 26, 2007

Software what?

Just saw this picture online and was thinking to myself..

well this day is not very far away and approaching us very soon. we are going to see "wanted software pros" like our current "open house" signs.

but I am curious how to interpret...
too much demand for software professionals?
or
would that be too much supply in market , that employing headhunters to recruit would be considered "not-worth-the-cost".

Take a look at this picture..




Saturday, November 3, 2007

ASM instance & remote client

well we know ASM instance is always in mount state and so you really dont have much access to data dictionary..and listener displays BLOCKED as status leaving you no choice but to login into the OS server and connect to asm instance locally..

But I just learned something new..that by adding "UR=A" to tnsnames, you can actually connect to asm instance (blocked service) via oracle net connection. (remote connection)

thats really cool thing for me..as I dont have to login to a server box just to check asm instance..I can simply have one more sql window open to asm instance from my client..

Add "(UR=A)" to the connect_data section of your tnsnames entry and you should be all set.

(Note: ASM instance lets admin authentication only via password file or OS authentication..so obviously, you have to have password file created before trying to connect to ASM from remote client)


Saturday, October 6, 2007

End of in-house DBAs?

just happened to read this interesting blog in searchoracle.com.
The article discusses about the latest evolution of "Oracle on demand" service and its potential impact (or end) of in-house DBAs

"....members revealed a surprisingly high 37% of you currently use hosted apps.

Does that concern you DBAs? Is this the beginning of the end of the in-house DBA?

For managers, Oracle’s pitch is compelling:

With more than 1.7 million users, including enterprise customers with the most rigorous requirements, Oracle On Demand simplifies enterprise computing by reducing the need to handle software upgrades, patches, and the day-to-day maintenance required to keep customer solutions available and secure.

. . . not to mention a lower TCO, including no six-figure salaries to those pesky senior DBAs. It’s the “best of all worlds” as the Oracle site melodramatically puts it.

"

and finally it posts the question

"Do you think that Oracle DBAs’ days are numbered because of the growth of On Demand?"

that was really interesting to think about. I don't personally foresee something like this to become a successful strategy in at least the next 4-5 years, unless Oracle changes its staff/team and strategy. Forget about data being hosted, currently ask anyone who has to deal with metalink folks.. It sometimes gives such a bad taste, you even wonder how the heck these guys managed to find a job in Oracle..

On the other hand, having a alternative is good for the company, as I have seen many DBAs who are not technically competent but simply want to enforce whatever be their principles. So this alternatives would eventually make them realize they are not the super-bosses anymore to say& act the way they wanted. CEO/CTO now has an option to bypass such egoistic persons in their companies.



Saturday, September 29, 2007

11g 's DBMS_SPM

11g introduces more admin-friendly features to maintain/control query plan stability.

I was going through this new package introduced in 11g for SQL PLAN Management(SPM) called DBMS_SPM.and to be honest, I actually got distracted while I was reading abt this.. Instead of knowing how to actually use this the way its intended to be used (many plans for a sql and flip flopping which one should the DBA wants to be used)..I thought something else and found it useful in a different way..


If you been to environments where developers just go nuts when
- they see the plan in production was completely different than what they expected
- they see a SQL taking forever because the plan changed in Production for no reason (most likely after stats gatheration or shutdown)

and there have been countless situations a DBA/Consultant would walk-in like an emergency doctor and take a look at all those "v$sql" patients and finally conclude "You are suffering from bind peeking problem". (which roughly means the plan in the shared pool was optimized for the first input value and unfit/inefficient for subsequent calls with varying input values)




Setup:
------

Sql>ed
Wrote file afiedt.buf

1 Create table test_sql_plan
2 as select 100 id, rpad(rownum,10,'x') name
3* from dual connect by level<999>/

Table created.

Sql>ed
Wrote file afiedt.buf

1 insert into test_sql_plan
2 select rownum id, rpad(rownum,10,'x') name
3* from dual connect by level<11>/

10 rows created.

Sql>commit;

Commit complete.

Sql>Create index test_sp_idx on test_sql_plan(id);

Index created.

Sql>begin
2 dbms_stats.gather_table_stats(tabname=>'TEST_SQL_PLAN',
3 ownname=>'VENKAT',
4 method_opt=>'for all indexed columns size 254');
5 end;
6 /

PL/SQL procedure successfully completed.




Testing begins from here...

Session 1:
----------
Sql>exec :v := 2;

PL/SQL procedure successfully completed.

Sql>select * from test_sql_plan plan_with_2_first where id=:v;

ID NAME
---------- ----------
2 2xxxxxxxxx


-- session 2
-- ----------
sq2>ed
Wrote file afiedt.buf

1 select sql_text sqltext,sql_id,plan_hash_value,executions from v$sql
2 where sql_text like
3 'select * from test_sql_plan plan_with_2_first%where id=%'
4* and executions>0
sq2>/

SQLTEXT SQL_ID PLAN_HASH_VALUE EXECUTIONS
--------------------------------------------- ------------- --------------- ----------
select * from test_sql_plan plan_with_2_first 69yujf79wwkja 3623521558 1
where id=:v

sq2>-- go back to session 1 and rerun the same sql for different bind value

Sql>exec :v := 100;

PL/SQL procedure successfully completed.

Sql>select * from test_sql_plan plan_with_2_first where id=:v;
...
...
...
100 991xxxxxxx
100 992xxxxxxx
100 993xxxxxxx
100 994xxxxxxx
100 995xxxxxxx
100 996xxxxxxx
100 997xxxxxxx
100 998xxxxxxx

998 rows selected.

Sql>

-- session 2
-- ----------
sq2>/

SQLTEXT SQL_ID PLAN_HASH_VALUE EXECUTIONS
--------------------------------------------- ------------- --------------- ----------
select * from test_sql_plan plan_with_2_first 69yujf79wwkja 3623521558 2
where id=:v

sq2>ed
Wrote file afiedt.buf

1 select OPERATION,OBJECT_OWNER,OBJECT_NAME from v$sql_plan
2 where PLAN_HASH_VALUE=3623521558
3 and sql_id='69yujf79wwkja'
4* order by id
sq2>/

OPERATION OBJECT_OWNER OBJECT_NAME
------------------------------ ------------------------------ -----------------------------
SELECT STATEMENT
TABLE ACCESS VENKAT TEST_SQL_PLAN
INDEX VENKAT TEST_SP_IDX








-- now we know we have a bad plan in shared pool ..
-- how do we go about and eliminate the plan
-- without brutally flushing the entire shared pool
--


DBMS_SPM to our rescue..

sq2>l
1 declare
2 lv_res PLS_INTEGER;
3 begin
4 lv_res:= DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE(sql_id=>'69yujf79wwkja');
5 dbms_output.put_line(lv_res);
6* end;
sq2>/
1

PL/SQL procedure successfully completed.

sq2>select sql_handle,sql_text from dba_sql_plan_baselines
2 where sql_text like
3 'select * from test_sql_plan plan_with_2_first%where id=%'
4 /

SQL_HANDLE SQL_TEXT
------------------------------ ----------------------------------------------------------
SYS_SQL_5053e5a52d822dcf select * from test_sql_plan plan_with_2_first where id=:v

sq2>declare
2 lv_res pls_integer;
3 begin
4 lv_res:=DBMS_SPM.DROP_SQL_PLAN_BASELINE (sql_handle=>'SYS_SQL_5053e5a52d822dcf');
5 dbms_output.put_line(lv_res);
6 end;
7 /
1

PL/SQL procedure successfully completed.



for bind variable 100, the sql was initially doing index lookup before (whereas full table scan was optimal one)

Lets run it again


--session 1 runs the same sql again (with value =100)
Sql>select * from test_sql_plan plan_with_2_first where id=:v;
...
...
...
100 991xxxxxxx
100 992xxxxxxx
100 993xxxxxxx
100 994xxxxxxx
100 995xxxxxxx
100 996xxxxxxx
100 997xxxxxxx
100 998xxxxxxx

998 rows selected.

-- session 2
sq2>select sql_text sqltext,sql_id,plan_hash_value,executions from v$sql
2 where sql_text like
3 'select * from test_sql_plan plan_with_2_first%where id=%'
4 and executions>0
5 /

SQLTEXT SQL_ID PLAN_HASH_VALUE EXECUTIONS
--------------------------------------------- ------------- --------------- ----------
select * from test_sql_plan plan_with_2_first 69yujf79wwkja 3623521558 2
where id=:v

select * from test_sql_plan plan_with_2_first 69yujf79wwkja 1269960815 1
where id=:v <---- ***** NEW PLAN *****

sq2>ed
Wrote file afiedt.buf

1 select OPERATION,OPTIONS,OBJECT_OWNER,OBJECT_NAME from v$sql_plan
2 where PLAN_HASH_VALUE=1269960815
3 and sql_id='69yujf79wwkja'
4* order by id
sq2>/

OPERATION OPTIONS OBJECT_OWNER OBJECT_NAME
------------------------------ ------------------------------ ------------------------------ -------
SELECT STATEMENT
TABLE ACCESS FULL VENKAT TEST_SQL_PLAN




Now that looks like easy fix without having to run dbms_stats or flushing shared pool or running an alter table (to forcefully invalidate the plan in shared pool)

Sunday, September 23, 2007

something new learnt (outside oracle)

finally I am becoming a little HTML-aware person..
not anymore you are going to see messy formats in this blog as I just learnt (& tested) I could use preserve tags to avoid spaces getting eaten up and making the sql code and display totally clumpsy.

here is a test..

copy and paste of same text (from sql screen)

old way (how It appeared before)

OPERATION OBJECT_OWNER
------------------------------ ------------------------------
SELECT STATEMENT
SELECT STATEMENT
SELECT STATEMENT
SELECT STATEMENT
SELECT STATEMENT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
INDEX VENKAT
INDEX VENKAT
INDEX VENKAT
INDEX VENKAT
INDEX VENKAT


new way (from now on)


OPERATION OBJECT_OWNER
------------------------------ ------------------------------
SELECT STATEMENT
SELECT STATEMENT
SELECT STATEMENT
SELECT STATEMENT
SELECT STATEMENT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
TABLE ACCESS VENKAT
INDEX VENKAT
INDEX VENKAT
INDEX VENKAT
INDEX VENKAT
INDEX VENKAT



Thats pretty neat!!