Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Roberto.

Asked: December 19, 2004 - 1:00 pm UTC

Last updated: June 03, 2009 - 12:10 pm UTC

Version: 8.1.7.4.1

Viewed 10K+ times! This question is

You Asked

Tom,
Thank you for taking the time to answer my question.The following query seems to be using bind variables yet -according to TKPROF- it is being parsed 18722 (the same number of times is being executed). Since bind variables are being used, I was expecting to see PARSE closer to 1. Why would this be happening? What could be causing this query to be parsed for each execution? For what is worth, the query is being issued from a program written in Centura Team Developer.

Also, Do you have any suggestions to improve its performance? This is where the program spends 70% of its time so making this section run faster will yield the most response time improvements.

Thank you very much, Tom.

Roberto.
********************************************************************************


select
r.status, NVL(r.required_date, to_date('12/31/3999', 'MM/DD/YYYY')),
r.calc_qty, r.issued_qty,
r.workorder_type, r.workorder_base_id, r.workorder_lot_id,
r.workorder_split_id, r.workorder_sub_id,
r.operation_seq_no, r.piece_no, r.warehouse_id, 0
from
REQUIREMENT r
where
r.part_id = :1
and r.workorder_type = 'W'
and r.calc_qty > r.issued_qty
and r.subord_wo_sub_id is null
AND STATUS IN ('R', 'S','U','H','F')

union

select
co.status,
NVL(NVL( ln.desired_ship_date, co.desired_ship_date ),
to_date('12/31/3999', 'MM/DD/YYYY')), ln.order_qty, ln.total_shipped_qty,
'C', ln.cust_order_id, to_char(LN.LINE_NO), '0', '0', 0, 0,
NVL( ln.warehouse_id, co.warehouse_id ), 0
from
CUST_ORDER_LINE ln, CUSTOMER_ORDER co
where
ln.cust_order_id = co.id
and ln.part_id = :2
and ln.order_qty > ln.total_shipped_qty
AND STATUS IN ('R', 'S','U','H','F')
AND LINE_STATUS = 'A'
union

select
co.status,
NVL(NVL( d.desired_ship_date, ln.desired_ship_date ), to_date('12/31/3999', 'MM/DD/YYYY')),
d.order_qty, d.shipped_qty,
'CD', ln.cust_order_id, to_char(LN.LINE_NO), '0', '0', 0, d.del_sched_line_no,
NVL( NVL( d.warehouse_id, ln.warehouse_id ), co.warehouse_id ), 0
from
CUST_ORDER_LINE ln, CUSTOMER_ORDER co, CUST_LINE_DEL d
where
ln.cust_order_id = co.id
and ln.cust_order_id = d.cust_order_id
and ln.line_no = d.cust_order_line_no
and ln.part_id = :3
and d.order_qty > NVL(d.shipped_qty, 0)
AND STATUS IN ('R', 'S','U','H','F')
AND LINE_STATUS = 'A'





union

select
null, NVL(pr.required_date, to_date('12/31/3999', 'MM/DD/YYYY')), pr.required_qty, 0,
'PL', pr.parent_part_id, to_char(PR.PARENT_SEQ_NO), '0', '0', 0, 0, pr.warehouse_id, pr.req_no
from
PLANNED_MATL_REQ pr
where
pr.required_part_id = :4



order by
2, 5

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 18722 4322.19 4332.99 0 0 0 0
Execute 18722 3.43 3.38 0 0 0 0
Fetch 67188 350.71 3758.89 1669876 25183424 39241 1060871
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 104632 4676.33 8095.26 1669876 25183424 39241 1060871

Misses in library cache during parse: 0
Parsing user id: 18 (SYSADM)

Rows Row Source Operation
------- ---------------------------------------------------
31 SORT UNIQUE
31 UNION-ALL
31 TABLE ACCESS BY INDEX ROWID REQUIREMENT
4481 INDEX RANGE SCAN (object id 3855)
0 NESTED LOOPS
1 TABLE ACCESS BY INDEX ROWID CUST_ORDER_LINE
1 INDEX RANGE SCAN (object id 3233)
0 TABLE ACCESS BY INDEX ROWID CUSTOMER_ORDER
0 INDEX UNIQUE SCAN (object id 3197)
0 NESTED LOOPS
1 HASH JOIN
0 TABLE ACCESS BY INDEX ROWID CUST_ORDER_LINE
1 INDEX RANGE SCAN (object id 3233)
0 TABLE ACCESS FULL CUST_LINE_DEL
0 TABLE ACCESS BY INDEX ROWID CUSTOMER_ORDER
0 INDEX UNIQUE SCAN (object id 3197)
0 INDEX RANGE SCAN (object id 24818)


Rows Execution Plan
------- ---------------------------------------------------
0 SELECT STATEMENT GOAL: CHOOSE
31 SORT (UNIQUE)
31 UNION-ALL
31 TABLE ACCESS GOAL: ANALYZED (BY INDEX ROWID) OF
'REQUIREMENT'
4481 INDEX GOAL: ANALYZED (RANGE SCAN) OF 'X_REQUIREMENT_1'
(NON-UNIQUE)
0 NESTED LOOPS
1 TABLE ACCESS GOAL: ANALYZED (BY INDEX ROWID) OF
'CUST_ORDER_LINE'
1 INDEX GOAL: ANALYZED (RANGE SCAN) OF 'X_ORDER_LINE_2'
(NON-UNIQUE)
0 TABLE ACCESS GOAL: ANALYZED (BY INDEX ROWID) OF
'CUSTOMER_ORDER'
0 INDEX GOAL: ANALYZED (UNIQUE SCAN) OF 'SYS_C001330'
(UNIQUE)
0 NESTED LOOPS
1 HASH JOIN
0 TABLE ACCESS GOAL: ANALYZED (BY INDEX ROWID) OF
'CUST_ORDER_LINE'
1 INDEX GOAL: ANALYZED (RANGE SCAN) OF
'X_ORDER_LINE_2' (NON-UNIQUE)
0 TABLE ACCESS GOAL: ANALYZED (FULL) OF 'CUST_LINE_DEL'
0 TABLE ACCESS GOAL: ANALYZED (BY INDEX ROWID) OF
'CUSTOMER_ORDER'
0 INDEX GOAL: ANALYZED (UNIQUE SCAN) OF 'SYS_C001330'
(UNIQUE)
0 INDEX GOAL: ANALYZED (RANGE SCAN) OF
'LWS_PLANNED_MATL_REQ_1' (NON-UNIQUE)

********************************************************************************

and Tom said...

parsing is something controlled by your programmers.

Oracle, when told to parse, will (must, cannot deny you) parse. The parse will be one of three kinds:

a) hard (bad)
b) soft (bad)
c) softer soft (still bad)

the only good parse is NO parse -- and your application developers totally control that!


According to your tkprof:

Misses in library cache during parse: 0

you had all soft (or softer soft) parsing going on -- but you are pointing out how expensive over parsing can be. Here you are spending *more time parsing sql* than you do *running sql*...

As Homer Simpson would say "DOH!"


the good news is, if this were hard parsed each time, your system would be dead right now, nothing would be happening.


See
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:2588723819082 <code>

for a definition of parsing. But what is happening is here your developers have decided to parse, bind, execute, close -- thousands and thousands of times.

Instead of

a) parse it once
b) bind/execute over and over

Ask them "so, would you compile a subroutine, run it, discard the compiled code and do it over and over each time you needed to call a function? of course not. so stop doing that in your SQL programming!"


The high cpu time for the soft parse leads me to believe this single instance here (this single tkprof) is but the tip of the very large iceberg. You probably have latch free waits through the roof and the high cpu here is caused not by "it takes long to soft parse" as much as "we had to spin and wait for the library cache latch for a long long time, burning cpu while doing so -- because SO MANY people are trying to simultaneously soft parse and the line to get into the shared pool was so very very very long"



You might be able to offset some of this by setting session cached cursors (init.ora or session parameter) to a value such as 100.

But - I'm actually doubtful, I'll hypothesize further. That they would run the same query thousands of times indicates to me that they took a very very procedural approach to their problem. Looks like they reinvented the nested loop join here (they are calling this code inside of a loop that is running yet another query. That would lead me to believe there are probably hundreds, if not thousands of these sorts of problems in this application all over the place. You might well have more cursors to cache than you want to cache using session cached cursors.


The problem lies in the developed code. It needs to be fixed to correct this problem entirely. If this is pro*c code, tell them to look at the pro*c parameters that control hold cursor (yes please), release cursor (no thank you) and max cursors (enough for your working set)

Rating

  (12 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Excellent.

Roberto, December 19, 2004 - 3:15 pm UTC

Thanks Tom. I actually have session cached cursors set to 150, so I guess we are on the same page there. And yes, there are other modules showing the same behaviour as this one. The application was not developed in-house. It is part of an ERP package that we own, so I can't have the code modified. I am being asked to make the system go faster, but I am struggling. With poor code I m not sure I have too much to work with. Anyway, any suggestions to make the query 'run faster'? Index, anything?

Thanks so much for your quick response.
R.

Tom Kyte
December 19, 2004 - 3:31 pm UTC

query is running lightspeed.

problem is too many people trying to parse into a SHARED data structure (eg: serialization = necessary) simultaneously. If you took this query on an idle machine and just looped:

loop
parse
close
end loop

a couple thousand times -- it would be lightspeed too I would guess. However, if you have dozens/hundreds/thousands of sessions doing that simultaneously -- you'll start hitting slow downs -- higher CPU times as more people try to simultaneously parse. We are spinning on latches -- waiting for our turn in the shared pool.

how closely do you work with the vendor? they issue goes down to the core of their code.

The vendor can remove over 4,000 cpu seconds for this query alone easily. You and I -- we cannot.

Open, parse, close

Loz, December 19, 2004 - 6:16 pm UTC

Tom,
In the world of web, Java, application servers & connection pools, how would you recommend using connections and prepared statements/bind variables? I can see the reason in what you are saying but with a connection pool everything gets closed when the request is finished which invalidates all the carefully prepared (parsed) statements. I assume this is what session_cached_cursors is attempting to achieve but you don't seem that confident it would make a huge difference.

Secondly, in a non-connection pooled application, for how long would you consider keeping the prepared statement? If we had one prepared statement per query per client would it cause a resource problem on the server, or are they client only resources?

Thanks.


Tom Kyte
December 19, 2004 - 6:33 pm UTC

see the jdbc quick tip:

</code> https://www.oracle.com/technetwork/issue-archive/2014/14-nov/o64asktom-2298498.html <code>

also -- programmers could have done this themselves -- they could have certainly associated a hash table with the retrieved connection handle.


but in this case, this is an application that is not using a connection pool I think, it was just written really "not good"


I would keep the statement open for as long as I possibly could -- I would use PLSQL and let PLSQL deal with it (plsql is awesome at the parse as little as you can, execute over and over and over concept). In fact, if you put all of your sql in PLSQL all you have to cache is the single call to the server, not the 15 statements it executes on your behalf!



Loz, December 19, 2004 - 7:10 pm UTC

Tom,
Thanks for the amazingly quick reply.
I read the article and am not sure still how I can fit this into the connection pool from our application server. Unfortunately the app server returns a delegate connection object rather than an actual OracleConnection. I therefore cannot call anything directly on the connection that is not on the JDBC Connection interface. Similarly, my application has no access to the connection pool itself so the OracleDataSource is also off limits.
Were this raw JDBC I'd be fine, but in this case I do not think it will work. Any other ideas? What's the deal with prepared statements on pooled connections? Does the connection need to be 'active' for that session, or will just a live connection do the job reliably?

Tom Kyte
December 19, 2004 - 7:16 pm UTC

you can do the same thing -- do you see what they are conceptually doing there?

they just have a hash table basically associated with the connection -- when you say "prepare" -- they look to see if it already is prepared in the hash table and use that, else they prepare it and add it.

you just DIY in this case (do it yourself)

or just use PLSQL :)

Loz, December 19, 2004 - 7:23 pm UTC

Yes I see what's going on. Just 2.5 more questions

1) What impact will my cache of unclosed statements have on the server? Presumably none or little.

2) If my cached statement's connection is busy with someone elses query when it gets reused, will I wait for that connection or will something go bang? Does it really matter?

2.5) Not really your thing I know, but how do you reconcile a cache with the <sarcasm>glorious</sarcasm> world of stateless sessions?

Tom Kyte
December 19, 2004 - 7:45 pm UTC

1) or postive even :) as your server does less needless work, it can do more needful work.

It'll increase the working set of statements -- as long as open cursors is set sufficiently high (and your logic could even handle the case where by you hit the ora-1000 and goto this hash table and free out the last 5 statements that haven't been used in a while...)

2) it cannot be -- that would physically be impossible. the connection pool would be fatally flawed if that was possible.

3) in this case, it shouldn't be a problem -- you have a set of appication(s) sharing a connection -- they grab it, use it and give it back. they are done with it.

Loz, December 19, 2004 - 10:09 pm UTC

I think in a multithreaded environment (such as an application server) there would need to be serialization on each statement while the parameters are set and the statement is executed. As parsing also requires serialization but on the database, there's a trade off to be made. My situation is complicated by the fact our back end database supplies different data depending on who gets the connection (we use getConnection(username, password) from the pool) so I suspect the cached statement would break if subsequent users are different from the first one. It sounds like a bit of experimenting is in order.
It's a shame that (session_cached_cursors=200) <> (faster=true), at least in this case.
Thanks for your help & comments.
L.


Tom Kyte
December 20, 2004 - 8:13 am UTC

the statements are associated with a CONNECTION

a connection is used by AT MOST one "thread" -- you grab a connection from a pool, NO ONE else is using that connection.




Rac env--session_cached_cursor impact

N.Venkatagiri, December 20, 2004 - 2:27 am UTC

for a RAC (3 instance each with 4 processors),
1000 users, OLTP env,
what would be the impact of setting the session_cached_cursor.
As we are on the real time
we can test the same after getting the advices.

session_cached_cursor=0 now.
Can we set 200..

Thanks


Tom Kyte
December 20, 2004 - 8:21 am UTC

RAC or not, the impact will be that you might be doing a softer soft parse if you are over parsing now. You might find parse times going down a bit, the number of latches taken getting reduced (but not disappearing alltogether, only "not parsing" can do that)

oracle dba

reader, January 31, 2006 - 11:00 am UTC

The response is excellent. I had more clear concept about parse after reading.

Rewrite query

Sergio Souza, October 20, 2008 - 9:04 pm UTC

Tom,
How can we rewrite the query posted from Roberto to minimize parse?
Tom Kyte
October 21, 2008 - 3:43 pm UTC

please re-read the original answer.

How a query is written (syntax) will not affect how many times it is parsed.

A program controls that. You cannot rewrite a query to minimize how many times that query is parsed, you have to get into the code itself, whatever client you are using.

Eg: the best way to reduce, minimize, cut down on parsing - stuff ALL SQL into plsql stored packages and have the application just call plsql.


PLSQL silently, transparently, magically caches our sql statements and only parses them when it absolutely has too.

But a programmer can do that do - if you are using java, search around for jdbc statement caching for example.

Bind variables and Parse Calls

yoav ben moha, June 01, 2009 - 2:12 am UTC

Hi Tom,
I found in my 30 minute awr report alot of statments that made a parse calls.
For example, the following statment executed 12,117 times,and each time had been parsed. why ? i am using binds as you can see :

12,117 12,117 2.66 6hj32q20bbz6j
Module:
delete tc_daily_counters where rowid = :1

And few more examples:
% Total
Parse Calls Executions Parses SQL Id
------------ ------------ --------- -------------
28,900 28,900 6.35 112k25pgmqtxx
Module:
SELECT to_char(last_day(add_months(to_date(:1,'yyyymmdd'),(-1) * :2)),'yyyymmdd') from dual

28,900 28,900 6.35 41rjjukjazn79
Module:
SELECT to_char(last_day(add_months(to_date(:1,'yyyymmdd'),(-1) * :2)),'yyyymmdd') from dual

23,941 23,941 5.26 c7k5c351yy3vp
Module:
SELECT to_char(last_day(add_months(to_date(:1,'yyyymmdd'), :2)),'yyyymmdd') from dual

12,117 12,117 2.66 6hj32q20bbz6j

Bellow are the statistics from the Load Profile for the 30minute awr report.

Load Profile
~~~~~~~~~~~~ Per Second Per Transaction
------------ ----------------
Redo size: 557,513.49 24,307.83
Logical reads: 18,639.93 812.71
Block changes: 3,039.32 132.52
Physical reads: 750.58 32.73
Physical writes: 442.75 19.30
User calls: 2,425.65 105.76
Parses: 250.15 10.91
Hard parses: 19.74 0.86
Sorts: 313.52 13.67
Logons: 0.19 0.01
Executes: 1,866.21 81.37
Transactions: 22.94

% Blocks changed per Read : 16.31 Recursive Call %: 41.57
Rollback per transaction %: 0.29 Rows per Sort: 4.75

Instance Efficiency Percentages (Target 100%)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Buffer Nowait %: 99.99 Redo NoWait %: 100.00
Buffer Hit %: 96.32 In-memory Sort %: 100.00
Library Hit %: 97.91 Soft Parse %: 92.11
Execute to Parse %: 86.60 Latch Hit %: 99.98
Parse CPU to Parse Elapsd %: 75.21 % Non-Parse CPU: 94.48

Thanks

Tom Kyte
June 01, 2009 - 8:06 pm UTC

do you understand that every time your programs call "prepare statement" that is a parse

do you know there are three types of parses in Oracle -

a) hard parse - very very very very bad, should be few of these in general
b) soft parse - very very very bad, a program has to parse AT LEAST once, but also AT MOST once the SQL it wants to execute

c) the ABSENCE of a parse, no parse, this is the only good parse


binds will turn a hard parse problem into a soft parse problem
good programmers that actually know how to spell "database" can turn a soft parse problem into a no-parse goodness.


soft parses are parses, they cost "less" than hard parses - but a parse is a parse by any other name....

Proc c binds

yoav ben moha, June 02, 2009 - 7:23 am UTC

Hi Tom ,

This is a pro*c program.
I asked my developer to check your above comment :
"If this is pro*c code, tell them to look at the pro*c parameters that control hold cursor (yes please), release cursor (no thank you) and max cursors (enough for your working set) "

Could you please be more specific about the names of those
parameters ? my developer dose not know these parameters.
(He used $ORACLE_HOME/precomp/demo/proc/sample10.pc as an example for his code)
Thank You
Tom Kyte
June 02, 2009 - 7:55 am UTC

does your developer know how to read documentation? Your developer hasn't read documentation yet, developer should do so now.

http://www.oracle.com/pls/db102/search?remark=quick_search&word=hold_cursor&tab_id=&format=ranked

for example - it magically points you right to the documentation for that...

Parse using pro*c

yoav ben moha, June 02, 2009 - 9:24 am UTC

Hi Tom,

Also, after adding the parameters still every statment is been parsed again and again.
Could you please help what we are missing hir ?
Thanks

For example:
proc hold_cursor=yes release_cursor=no iname=sample10 include=. include=/software/oracle/BILDEV10gR2/precomp/public include=/software/oracle/BILDEV10gR2/rdbms/public include=/software/oracle/BILDEV10gR2/rdbms/demo include=/software/oracle/BILDEV10gR2/plsql/public include=/software/oracle/BILDEV10gR2/network/public

We execute the follwoing statment 5 times and each time is been parsed.

select *
from
dual where dummy = &1


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 5 0.00 0.00 0 0 0 0
Execute 5 0.00 0.00 0 0 0 0
Fetch 6 0.00 0.00 0 15 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 16 0.00 0.00 0 15 0 1

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: FIRST_ROWS
Parsing user id: 13

Rows Row Source Operation
------- --------------------------------------------------
1 TABLE ACCESS FULL DUAL (cr=3 pr=0 pw=0 time=58 us)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 12 0.00 0.00
SQL*Net message from client 12 18.91 52.27
***********************************************************

The trace file show:
=====================
PARSING IN CURSOR #4 len=36 dep=0 uid=13 oct=3 lid=13 tim=2520335486743 hv=2369842311 ad='a2666b28'
select * from dual where dummy = &1
END OF STMT
PARSE #4:c=0,e=989,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=2,tim=2520335486736
WAIT #4: nam='SQL*Net message to client' ela= 2 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520335486835
*** 2009-06-02 15:46:32.000
WAIT #4: nam='SQL*Net message from client' ela= 1143274 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520336630136
=====================
PARSING IN CURSOR #3 len=210 dep=1 uid=0 oct=3 lid=0 tim=2520336631769 hv=864012087 ad='af9a4fc0'
select /*+ rule */ bucket_cnt, row_cnt, cache_cnt, null_cnt, timestamp#, sample_size, minimum, maximum, distcnt, lowval, hival, density, col#, spare1, spare2, avgcln from hist_head$ where obj#=:1 and intcol#=:2
END OF STMT
PARSE #3:c=0,e=827,p=0,cr=0,cu=0,mis=1,r=0,dep=1,og=3,tim=2520336631765
BINDS #3:
kkscoacd
Bind#0
oacdty=02 mxl=22(22) mxlc=00 mal=00 scl=00 pre=00
1,1 Top
select * from dual where dummy = &1
END OF STMT
PARSE #4:c=0,e=25,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=2,tim=2520346684589
WAIT #4: nam='SQL*Net message to client' ela= 2 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520346684679
WAIT #4: nam='SQL*Net message from client' ela= 5053734 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520351738447
BINDS #4:
kkscoacd
Bind#0
oacdty=01 mxl=32(01) mxlc=00 mal=00 scl=00 pre=00
oacflg=21 fl2=1000000 frm=01 csi=38 siz=32 off=0
kxsbbbfp=9fffffffbf3c41e0 bln=32 avl=01 flg=05
value="y"
EXEC #4:c=0,e=163,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=2,tim=2520351738718
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520351738785
FETCH #4:c=0,e=89,p=0,cr=3,cu=0,mis=0,r=0,dep=0,og=2,tim=2520351738905
*** 2009-06-02 15:46:53.502
WAIT #4: nam='SQL*Net message from client' ela= 5889248 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520357628227
STAT #4 id=1 cnt=0 pid=0 pos=1 obj=72881 op='TABLE ACCESS FULL DUAL (cr=3 pr=0 pw=0 time=85 us)'
=====================
PARSING IN CURSOR #4 len=36 dep=0 uid=13 oct=3 lid=13 tim=2520357628472 hv=2369842311 ad='a2666b28'
select * from dual where dummy = &1
END OF STMT
PARSE #4:c=0,e=32,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=2,tim=2520357628466
WAIT #4: nam='SQL*Net message to client' ela= 3 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520357628558
WAIT #4: nam='SQL*Net message from client' ela= 1711282 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520359339871
BINDS #4:
kkscoacd
Bind#0
oacdty=01 mxl=32(01) mxlc=00 mal=00 scl=00 pre=00
oacflg=21 fl2=1000000 frm=01 csi=38 siz=32 off=0
kxsbbbfp=9fffffffbf3c41e0 bln=32 avl=01 flg=05
value="w"
EXEC #4:c=0,e=152,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=2,tim=2520359340122
WAIT #4: nam='SQL*Net message to client' ela= 1 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520359340173
FETCH #4:c=0,e=85,p=0,cr=3,cu=0,mis=0,r=0,dep=0,og=2,tim=2520359340288
*** 2009-06-02 15:47:14.620
WAIT #4: nam='SQL*Net message from client' ela= 18910928 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520378251283
STAT #4 id=1 cnt=0 pid=0 pos=1 obj=72881 op='TABLE ACCESS FULL DUAL (cr=3 pr=0 pw=0 time=82 us)'
=====================
PARSING IN CURSOR #4 len=55 dep=0 uid=13 oct=42 lid=13 tim=2520378251654 hv=2655499671 ad='0'
ALTER SESSION SET EVENTS '10046 trace name context off'
END OF STMT
PARSE #4:c=0,e=141,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,tim=2520378251649
WAIT #4: nam='SQL*Net message to client' ela= 2 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520378251732
WAIT #4: nam='SQL*Net message from client' ela= 174 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=2520378251930
EXEC #4:c=0,e=79,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=0,tim=2520378252036


Tom Kyte
June 02, 2009 - 6:35 pm UTC

&1?

show me the pro*c code please.

Pro*c binds and parses

yoav ben moha, June 03, 2009 - 4:17 am UTC

Hi Tom,
The code is base on the $ORACLE_HOME/precomp/demo/proc/sample10.pc
Hir is the whole code and how i execute it.
Thank You

/*******************************************************************
Sample Program 10:  Dynamic SQL Method 4

This program connects you to ORACLE using your username and
password, then prompts you for a SQL statement.  You can enter
any legal SQL statement.  Use regular SQL syntax, not embedded SQL.
Your statement will be processed.  If it is a query, the rows
fetched are displayed.
You can enter multi-line statements.  The limit is 1023 characters.
This sample program only processes up to MAX_ITEMS bind variables and
MAX_ITEMS select-list items.  MAX_ITEMS is #defined to be 40.
*******************************************************************/

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <sqlda.h>
#include <stdlib.h>
#include <sqlcpr.h>

/* Maximum number of select-list items or bind variables. */
#define MAX_ITEMS         40

/* Maximum lengths of the _names_ of the
   select-list items or indicator variables. */
#define MAX_VNAME_LEN     30
#define MAX_INAME_LEN     30

#ifndef NULL
#define NULL  0
#endif

/* Prototypes */
#if defined(__STDC__)
  void sql_error(void);
  int oracle_connect(void);
  int alloc_descriptors(int, int, int);
  int get_dyn_statement(void);
  void set_bind_variables(void);
  void process_select_list(void);
  void help(void);
#else
  void sql_error(/*_ void _*/);
  int oracle_connect(/*_ void _*/);
  int alloc_descriptors(/*_ int, int, int _*/);
  int get_dyn_statement(/* void _*/);
  void set_bind_variables(/*_ void -*/);
  void process_select_list(/*_ void _*/);
  void help(/*_ void _*/);
#endif

char *dml_commands[] = {"SELECT", "select", "INSERT", "insert",
                        "UPDATE", "update", "DELETE", "delete"};

EXEC SQL INCLUDE sqlda;
EXEC SQL INCLUDE sqlca;

EXEC SQL BEGIN DECLARE SECTION;
    char    dyn_statement[1024];
    EXEC SQL VAR dyn_statement IS STRING(1024);
EXEC SQL END DECLARE SECTION;
 
SQLDA *bind_dp;
SQLDA *select_dp;

/* Define a buffer to hold longjmp state info. */
jmp_buf jmp_continue;

/* A global flag for the error routine. */
int parse_flag = 0;

void main()
{
    int i;

    /* Connect to the database. */
    if (oracle_connect() != 0)
        exit(1);

    /* Allocate memory for the select and bind descriptors. */
    if (alloc_descriptors(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN) != 0)
        exit(1);

    /* Process SQL statements. */
    for (;;) 
    {
        (void) setjmp(jmp_continue);

        /* Get the statement.  Break on "exit". */
        if (get_dyn_statement() != 0)
            break;

        /* Prepare the statement and declare a cursor. */
        EXEC SQL WHENEVER SQLERROR DO sql_error();

        parse_flag = 1;     /* Set a flag for sql_error(). */
        EXEC SQL PREPARE S FROM :dyn_statement;
        parse_flag = 0;     /* Unset the flag. */

        EXEC SQL DECLARE C CURSOR FOR S;

        /* Set the bind variables for any placeholders in the
           SQL statement. */
        set_bind_variables();

        /* Open the cursor and execute the statement.
         * If the statement is not a query (SELECT), the
         * statement processing is completed after the
         * OPEN.
         */

        EXEC SQL OPEN C USING DESCRIPTOR bind_dp;

        /* Call the function that processes the select-list.
         * If the statement is not a query, this function
         * just returns, doing nothing.
         */
        process_select_list();

        /* Tell user how many rows processed. */
        for (i = 0; i < 8; i++)
        {
           if (strncmp(dyn_statement, dml_commands[i], 6) == 0)
           {
               printf("\n\n%d row%c processed.\n", sqlca.sqlerrd[2],
                       sqlca.sqlerrd[2] == 1 ? '\0' : 's');
               break;
           }
        }
    }       /* end of for(;;) statement-processing loop */

    /* When done, free the memory allocated for
       pointers in the bind and select descriptors. */
    for (i = 0; i < MAX_ITEMS; i++)
    {    
        if (bind_dp->V[i] != (char *) 0)
            free(bind_dp->V[i]);
        free(bind_dp->I[i]);   /* MAX_ITEMS were allocated. */
        if (select_dp->V[i] != (char *) 0)
            free(select_dp->V[i]);
        free(select_dp->I[i]); /* MAX_ITEMS were allocated. */
    }

    /* Free space used by the descriptors themselves. */
    sqlclu(bind_dp);
    sqlclu(select_dp);

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    /* Close the cursor. */
    EXEC SQL CLOSE C;

    EXEC SQL COMMIT WORK RELEASE;
    puts("\nHave a good day!\n");

    EXEC SQL WHENEVER SQLERROR DO sql_error();
    return;
}


int oracle_connect()
{
    EXEC SQL BEGIN DECLARE SECTION;
        VARCHAR  username[128];
        VARCHAR  password[32];
    EXEC SQL END DECLARE SECTION;

    printf("\nusername: ");
    fgets((char *) username.arr, sizeof username.arr, stdin);
    username.arr[strlen((char *) username.arr)-1] = '\0';
    username.len = (unsigned short)strlen((char *) username.arr);

    printf("password: ");
    fgets((char *) password.arr, sizeof password.arr, stdin);
    password.arr[strlen((char *) password.arr) - 1] = '\0';
    password.len = (unsigned short)strlen((char *) password.arr);


    EXEC SQL WHENEVER SQLERROR GOTO connect_error;

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    printf("\nConnected to ORACLE as user %s.\n", username.arr);

    return 0;

connect_error:
    fprintf(stderr, "Cannot connect to ORACLE as user %s\n", username.arr);
    return -1;
}


/*
 *  Allocate the BIND and SELECT descriptors using sqlald().
 *  Also allocate the pointers to indicator variables
 *  in each descriptor.  The pointers to the actual bind
 *  variables and the select-list items are realloc'ed in
 *  the set_bind_variables() or process_select_list()
 *  routines.  This routine allocates 1 byte for select_dp->V[i]
 *  and bind_dp->V[i], so the realloc will work correctly.
 */

alloc_descriptors(size, max_vname_len, max_iname_len)
int size;
int max_vname_len;
int max_iname_len;
{
    int i;

    /*
     * The first sqlald parameter determines the maximum number of
     * array elements in each variable in the descriptor. In
     * other words, it determines the maximum number of bind
     * variables or select-list items in the SQL statement.
     *
     * The second parameter determines the maximum length of
     * strings used to hold the names of select-list items
     * or placeholders.  The maximum length of column 
     * names in ORACLE is 30, but you can allocate more or less
     * as needed.
     *
     * The third parameter determines the maximum length of
     * strings used to hold the names of any indicator
     * variables.  To follow ORACLE standards, the maximum
     * length of these should be 30.  But, you can allocate
     * more or less as needed.
     */

    if ((bind_dp =
            sqlald(size, max_vname_len, max_iname_len)) == (SQLDA *) 0)
    {
        fprintf(stderr,
            "Cannot allocate memory for bind descriptor.");
        return -1;  /* Have to exit in this case. */
    }

    if ((select_dp =
        sqlald (size, max_vname_len, max_iname_len)) == (SQLDA *) 0)
    {
        fprintf(stderr,
            "Cannot allocate memory for select descriptor.");
        return -1;
    }
    select_dp->N = MAX_ITEMS;

    /* Allocate the pointers to the indicator variables, and the
       actual data. */
    for (i = 0; i < MAX_ITEMS; i++) {
        bind_dp->I[i] = (short *) malloc(sizeof (short));
        select_dp->I[i] = (short *) malloc(sizeof(short));
        bind_dp->V[i] = (char *) malloc(1);
        select_dp->V[i] = (char *) malloc(1);
    }
       
    return 0;
}


int get_dyn_statement()
{
    char *cp, linebuf[256];
    int iter, plsql;


    for (plsql = 0, iter = 1; ;)
    {
        if (iter == 1)
        {
            printf("\nSQL> ");
            dyn_statement[0] = '\0';
        }
        
        fgets(linebuf, sizeof linebuf, stdin);

        cp = strrchr(linebuf, '\n');
        if (cp && cp != linebuf)
            *cp = ' ';
        else if (cp == linebuf)
            continue;

        if ((strncmp(linebuf, "EXIT", 4) == 0) ||
            (strncmp(linebuf, "exit", 4) == 0))
        {
            return -1;
        }

        else if (linebuf[0] == '?' ||
            (strncmp(linebuf, "HELP", 4) == 0) ||
            (strncmp(linebuf, "help", 4) == 0))
        {
            help();
            iter = 1;
            continue;
        }

        if (strstr(linebuf, "BEGIN") ||
            (strstr(linebuf, "begin")))
        {
            plsql = 1;
        }

        strcat(dyn_statement, linebuf);

        if ((plsql && (cp = strrchr(dyn_statement, '/'))) ||
            (!plsql && (cp = strrchr(dyn_statement, ';'))))
        {
            *cp = '\0';
            break;
        }
        else
        {
            iter++;
            printf("%3d  ", iter);
        }
    }
    return 0;
}



void set_bind_variables()
{
    int i, n;
    char bind_var[64];

    /* Describe any bind variables (input host variables) */
    EXEC SQL WHENEVER SQLERROR DO sql_error();

    bind_dp->N = MAX_ITEMS;  /* Initialize count of array elements. */
    EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp;

    /* If F is negative, there were more bind variables
       than originally allocated by sqlald(). */
    if (bind_dp->F < 0)
    {
        printf ("\nToo many bind variables (%d), maximum is %d\n.",
                    -bind_dp->F, MAX_ITEMS);
        return;
    }

    /* Set the maximum number of array elements in the
       descriptor to the number found. */
    bind_dp->N = bind_dp->F;
 
    /* Get the value of each bind variable as a
     * character string.
     *   
     * C[i] contains the length of the bind variable
     *      name used in the SQL statement.
     * S[i] contains the actual name of the bind variable
     *      used in the SQL statement.
     *
     * L[i] will contain the length of the data value
     *      entered.
     *
     * V[i] will contain the address of the data value
     *      entered.
     *
     * T[i] is always set to 1 because in this sample program
     *      data values for all bind variables are entered
     *      as character strings.
     *      ORACLE converts to the table value from CHAR.
     *
     * I[i] will point to the indicator value, which is
     *      set to -1 when the bind variable value is "null".
     */
    for (i = 0; i < bind_dp->F; i++)
    {
        printf ("\nEnter value for bind variable %.*s:  ",
               (int)bind_dp->C[i], bind_dp->S[i]);
        fgets(bind_var, sizeof bind_var, stdin);

        /* Get length and remove the new line character. */
        n = strlen(bind_var) - 1;

        /* Set it in the descriptor. */
        bind_dp->L[i] = n;

        /* (re-)allocate the buffer for the value.
           sqlald() reserves a pointer location for
           V[i] but does not allocate the full space for
           the pointer. */

         bind_dp->V[i] = (char *) realloc(bind_dp->V[i],
                         (bind_dp->L[i] + 1));            

        /* And copy it in. */
        strncpy(bind_dp->V[i], bind_var, n);

        /* Set the indicator variable's value. */
        if ((strncmp(bind_dp->V[i], "NULL", 4) == 0) ||
                (strncmp(bind_dp->V[i], "null", 4) == 0))
            *bind_dp->I[i] = -1;
        else
            *bind_dp->I[i] = 0;
    
        /* Set the bind datatype to 1 for CHAR. */
        bind_dp->T[i] = 1;
    }
  return;
}



void process_select_list()
{
    int i, null_ok, precision, scale;

    if ((strncmp(dyn_statement, "SELECT", 6) != 0) &&
        (strncmp(dyn_statement, "select", 6) != 0))
    {
        select_dp->F = 0;
        return;
    }

    /* If the SQL statement is a SELECT, describe the
        select-list items.  The DESCRIBE function returns
        their names, datatypes, lengths (including precision
        and scale), and NULL/NOT NULL statuses. */

    select_dp->N = MAX_ITEMS;
    
    EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;

    /* If F is negative, there were more select-list
       items than originally allocated by sqlald(). */
    if (select_dp->F < 0)
    {
        printf ("\nToo many select-list items (%d), maximum is %d\n",
                -(select_dp->F), MAX_ITEMS);
        return;
    }

    /* Set the maximum number of array elements in the
       descriptor to the number found. */
    select_dp->N = select_dp->F;

    /* Allocate storage for each select-list item.
  
       sqlprc() is used to extract precision and scale
       from the length (select_dp->L[i]).

       sqlnul() is used to reset the high-order bit of
       the datatype and to check whether the column
       is NOT NULL.

       CHAR    datatypes have length, but zero precision and
               scale.  The length is defined at CREATE time.

       NUMBER  datatypes have precision and scale only if
               defined at CREATE time.  If the column
               definition was just NUMBER, the precision
               and scale are zero, and you must allocate
               the required maximum length.

       DATE    datatypes return a length of 7 if the default
               format is used.  This should be increased to
               9 to store the actual date character string.
               If you use the TO_CHAR function, the maximum
               length could be 75, but will probably be less
               (you can see the effects of this in SQL*Plus).

       ROWID   datatype always returns a fixed length of 18 if
               coerced to CHAR.

       LONG and
       LONG RAW datatypes return a length of 0 (zero),
               so you need to set a maximum.  In this example,
               it is 240 characters.

       */
    
    printf ("\n");
    for (i = 0; i < select_dp->F; i++)
    {
        char title[MAX_VNAME_LEN]; 
        /* Turn off high-order bit of datatype (in this example,
           it does not matter if the column is NOT NULL). */
        sqlnul ((unsigned short *)&(select_dp->T[i]), (unsigned short *)&(select_dp->T[i]), &null_ok);

        switch (select_dp->T[i])
        {
            case  1 : /* CHAR datatype: no change in length
                         needed, except possibly for TO_CHAR
                         conversions (not handled here). */
                break;
            case  2 : /* NUMBER datatype: use sqlprc() to
                         extract precision and scale. */
                sqlprc ((unsigned int *)&(select_dp->L[i]), &precision, &scale);
                      /* Allow for maximum size of NUMBER. */
                if (precision == 0) precision = 40;
                      /* Also allow for decimal point and
                         possible sign. */
                /* convert NUMBER datatype to FLOAT if scale > 0,
                   INT otherwise. */
                if (scale > 0)
                    select_dp->L[i] = sizeof(float);
                else
                    select_dp->L[i] = sizeof(int);
                break;

            case  8 : /* LONG datatype */
                select_dp->L[i] = 240;
                break;

            case 11 : /* ROWID datatype */
            case 104 : /* Universal ROWID datatype */
                select_dp->L[i] = 18;
                break;

            case 12 : /* DATE datatype */
                select_dp->L[i] = 9;
                break;
 
            case 23 : /* RAW datatype */
                break;

            case 24 : /* LONG RAW datatype */
                select_dp->L[i] = 240;
                break;
        }
        /* Allocate space for the select-list data values.
           sqlald() reserves a pointer location for
           V[i] but does not allocate the full space for
           the pointer.  */

         if (select_dp->T[i] != 2)
           select_dp->V[i] = (char *) realloc(select_dp->V[i],
                                    select_dp->L[i] + 1);  
         else
           select_dp->V[i] = (char *) realloc(select_dp->V[i],
                                    select_dp->L[i]);  

        /* Print column headings, right-justifying number
            column headings. */
        
        /* Copy to temporary buffer in case name is null-terminated */
        memset(title, ' ', MAX_VNAME_LEN);
        strncpy(title, select_dp->S[i], select_dp->C[i]);
        if (select_dp->T[i] == 2)
           if (scale > 0)
             printf ("%.*s ", select_dp->L[i]+3, title);
           else
             printf ("%.*s ", select_dp->L[i], title);
        else
          printf("%-.*s ", select_dp->L[i], title);

        /* Coerce ALL datatypes except for LONG RAW and NUMBER to
           character. */
        if (select_dp->T[i] != 24 && select_dp->T[i] != 2)
            select_dp->T[i] = 1;

        /* Coerce the datatypes of NUMBERs to float or int depending on
           the scale. */
        if (select_dp->T[i] == 2)
          if (scale > 0)
             select_dp->T[i] = 4;  /* float */
          else
             select_dp->T[i] = 3;  /* int */
    }
    printf ("\n\n");

    /* FETCH each row selected and print the column values. */
    EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;

    for (;;)
    {
        EXEC SQL FETCH C USING DESCRIPTOR select_dp;

        /* Since each variable returned has been coerced to a
           character string, int, or float very little processing 
           is required here.  This routine just prints out the 
           values on the terminal. */
        for (i = 0; i < select_dp->F; i++)
        {
            if (*select_dp->I[i] < 0)
                if (select_dp->T[i] == 4) 
                  printf ("%-*c ",(int)select_dp->L[i]+3, ' ');
                else
                  printf ("%-*c ",(int)select_dp->L[i], ' ');
            else
                if (select_dp->T[i] == 3)     /* int datatype */
                  printf ("%*d ", (int)select_dp->L[i], 
                                 *(int *)select_dp->V[i]);
                else if (select_dp->T[i] == 4)     /* float datatype */
                  printf ("%*.2f ", (int)select_dp->L[i], 
                                 *(float *)select_dp->V[i]);
                else                          /* character string */
                  printf ("%-*.*s ", (int)select_dp->L[i],
                            (int)select_dp->L[i], select_dp->V[i]);
        }
        printf ("\n");
    }
end_select_loop:
    return;
}



void help()
{
    puts("\n\nEnter a SQL statement or a PL/SQL block at the SQL> prompt.");
    puts("Statements can be continued over several lines, except");
    puts("within string literals.");
    puts("Terminate a SQL statement with a semicolon.");
    puts("Terminate a PL/SQL block (which can contain embedded semicolons)");
    puts("with a slash (/).");
    puts("Typing \"exit\" (no semicolon needed) exits the program.");
    puts("You typed \"?\" or \"help\" to get this message.\n\n");
}


void sql_error()
{
    /* ORACLE error handler */
    printf ("\n\n%.70s\n",sqlca.sqlerrm.sqlerrmc);
    if (parse_flag)
        printf
        ("Parse error at character offset %d in SQL statement.\n",
           sqlca.sqlerrd[4]);

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK WORK;
    longjmp(jmp_continue, 1);
}


$ 
$ ./sample10

username: xxxx
password: xxxx

Connected to ORACLE as user test.

SQL> select * from dual where dummy = &1 ;

Enter value for bind variable 1:  X
D 
X 
1 row processed.

SQL> exit
Have a good day!

$

Tom Kyte
June 03, 2009 - 12:10 pm UTC

answer is: you are doing dynamic sql, those parameters only control the behavior of static sql. when you do dynamic - we have NO CLUE what sql you are executing with each call - you cannot cache things when you have no idea what is being done.

with dynamic sql - you are 100% in control, if you don't want to parse and parse over and over - you would NOT CLOSE the statement.


Or use static sql.