Skip to Main Content

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Quinn.

Asked: May 18, 2000 - 10:47 am UTC

Last updated: August 28, 2013 - 5:38 pm UTC

Version: 8.1.5

Viewed 50K+ times! This question is

You Asked

Tom, sorry about that I figured it out finally. Thanks. -quinn




I am somewhat familiar with dynamic sql and
can substitute values in the sql e.g.

st := 'select acno from prod.acc where acp = ''PI'' and acno = :1';

execute immediate st into acno using 500000;

dbms_output.put_line ('st = '||st);
dbms_output.put_line (' acno = '||to_char(acno));

The above is just for example, but what I really want to
substitute is the
table name and column name e.g.


st := 'select max (:1) from :2';
execute immediate st into acno using 'acno','prod.acc';


and Tom said...

Identifiers cannot be used as bind variables -- ever. Identifiers must be "hard coded" into the query.

To test if something can be used as a bind variable -- you only have to consider if you could go into sqlplus and use a character string constant in that place. If you can -- you can use a bind variable. If you cannot -- you cannot use a bind variable there. For example:

SQL> select * from emp where ename = 'KING';

I can put a bind variable in place of 'KING' because I can put a character string constant there. On the other hand:

SQL> select * from 'emp' where ename = 'KING';

I cannot put a bind variable in for 'emp' because I cannot use a character string constant for emp.

The reason is quite simple -- a query with bind variables is parsed and optimized once before the bind variables are evaluated. If I could code:

select * from :x

the optimzer would not have any idea what plan to come up with, security (access rights) could not be evaluated, and so on. We could not develop a plan for that query -- not enough information. Therefore, no bind variables for identifiers.

Your example should be:

st := 'select max(' || 'acno' || ') from ' || 'prod.acc';
execute immediate st into acno;




Rating

  (224 ratings)

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

Comments

one more dynamic dynamic query

nagaraju, March 06, 2002 - 3:46 pm UTC

tom,

in this following how do we do it.

declare

a number;
b varchar2(1) : 'c';
c varcha2(2) := 'XX';
d varchar2(2) := 'YY';
e varchar2(2);

begin

if a is null then
b := 'c';
else
b := 'd';
end if;

execute immediate 'select ' || b || ' from dual ' into e;

end;

i get an error invalid column name.

can't we refer dynamaically a variable. i know in forms we do it using name_in. is there any way to do this.

nagaraju




Tom Kyte
March 06, 2002 - 4:22 pm UTC

execute immediate 'select :x from dual' INTO e USING b;

i need to refer the value of the variable dynamically not thename

nagaraju, March 06, 2002 - 4:41 pm UTC

i run it.

declare
2
3 a number;
4 b varchar2(1) := 'c';
5 c varchar2(2) := 'XX';
6 d varchar2(2) := 'YY';
7 e varchar2(2);
8
9 begin
10
11 if a is null then
12 b := 'c';
13 else
14 b := 'd';
15 end if;
16 execute immediate 'select :x from dual' INTO e USING b;
17 dbms_output.put_line(e);
18 end;
19 /
c

PL/SQL procedure successfully completed

i need the value of the variable c i.e. 'XX'..

again like you refer int FORMS 4.5 ONWARDS NAME_IN OR COPY functions

Tom Kyte
March 06, 2002 - 7:44 pm UTC

Oh, well, that was not at all clear.

You can ONLY do this with package global variables -- not local variables.

If you had:

create package demo_pkg
as
a number
b .....
e varchar2(2);
end;
/

then

execute immediate 'begin :x := demo_pkg.' || demo_pkg.b || '; end;'
using OUT demo_pkg.e;


would put into demo_pkg.e the value of demo_pkg.c if demo_pkg.b had the value of C in it.

I guess this fall sunder the perview of dynamic sql

Nag, June 12, 2002 - 5:05 pm UTC

I have table t which contains tablenames.

I need to check if value 'xyz' EXISTS IN a column which exists in all these tables. The column name in which value 'xyz' exists in all these tables is CHECK_VERIFY.

What is the best way to select all these table names out of
table t and verify if such values exists in column CHECK_VERIFY.

I cant do it with a regular cursor loop style as I cannot use a cursor value as table name in the from clause..

How should I do it using dynamic sql..

Thanks

Tom Kyte
June 13, 2002 - 7:59 am UTC



for x in ( select table_name from T )
loop
execute immediate
'select count(*)
from dual
where exists ( select null
from ' || x.table_name || '
where check_value = :x'
USING l_some_value
INTO l_cnt;

if ( l_cnt > 0 )
then
it exists
else
it does not
end if;
end loop;



did not actually run that -- but it's the gist of the answer (might not compile, but it is very close)

Interesting question

A reader, June 13, 2002 - 4:47 pm UTC

I tried to use your code in Reports . But it complained.
can execute immediate be used in oracle reports and oracle forms

Tom Kyte
June 13, 2002 - 6:08 pm UTC

execute immediate requires the plsql engine that comes with 8i (the 8.1 plsql release).

You can put it in a stored procedure and call it.

sqlplus, reports, forms

A reader, June 13, 2002 - 6:16 pm UTC

Does sqlplus, reports, forms have an inbuilt pl/sql engine.
I could not use execute immediate from reports, so it obvious that it does not have pl/sql engine.

Does sqlplus have a pl/sql engine or does forms does.

So is it like that, the clients have only sql engines so that sql is checked for syntax, and when it is sent to the database, the sql and pl/sql are separated and processed by the respective engines.

Please clarify. This understanding will help a lot.

Tom Kyte
June 13, 2002 - 6:31 pm UTC

reports and forms have their own engine. that is it.

sqplus does not, sqlplus is simply an interface to submit sql/plsql to the database.

the reports/forms clients have BOTH a client side plsql engine and a server side engine. They can use both / either or.



why does execute immediate require a pl/sql engine

Nag, June 13, 2002 - 6:22 pm UTC

and

usually there is a database engine containing both sql and pl/sql parser, and the clients will contain only the sql parser. I'm I right?


(Do you cover the sqlengine and plsql engine , and the database engine stuff anywhere in your book.)

Tom Kyte
June 13, 2002 - 6:32 pm UTC

the plsql "engine" is in both reports and forms -- as well as the database.

execute immediate is a plsql "verb", that is why it requires a plsql engine.

WELL!

Nag, June 13, 2002 - 9:50 pm UTC

Tom

1.so can we conclude that the pl/sql engine in oracle forms and reports 6i is not 8.1 , as such we cannot use execute immediate in them..
2.Well, then what is the version of pl/sql engine in oracle forms and reports 6i?

3.execute immediate is a plsql "verb", -- do you mean that execute immediate is a plsql construct when you say 'verb'

Tom Kyte
June 14, 2002 - 7:16 am UTC

1) in the releases you have, yes. when you start these tools up, they tell you what versions they are using (and help -> about should tell you as well)

2) use help -> about to get the definitive answer

3) yes.

A reader, June 13, 2002 - 11:00 pm UTC

Is this PL/SQL Engine just a compiler of PL/SQL code

Tom Kyte
June 14, 2002 - 7:17 am UTC

and interpreter (runtime)

it compiles it
it runs it.

Coolest

Rahul, July 23, 2002 - 5:56 am UTC

Hi Tom 

There is little problem for you...

when I execute the below procedure it generates a dynamic insert and executed but givig following error....

SQL> EXEC SP_COPYAUDITDATA_V1
DELETE
INSERT INTO CODE_AUDIT VALUES( '801','TEST','TEST
TEST','','AUD','','1','23-jul-02','7832','D',SEQ_CODE_AUDIT.NEXTVAL);
BEGIN SP_COPYAUDITDATA_V1; END;

*
ERROR at line 1:
ORA-00911: invalid character
ORA-06512: at "FPIMLVER103.SP_COPYAUDITDATA_V1", line 84
ORA-06512: at line 1

but when I copied the same insert statement and and run it explicitly from
sql*plus it works fine as u can see below..

SQL> INSERT INTO CODE_AUDIT VALUES(
'801','TEST','TEST
TEST','','AUD','','1','23-jul-02','7832','D',SEQ_CODE_AUDIT.NEXTVAL);

Pls. tell me where is the problem...

I'm sending each value with in quotes because avoiding to identify the char
and number becasue oracle implicitly treats the number in single quotes as
numeric value
and except number everything should be in quotes.

Thanks. 

Tom Kyte
July 23, 2002 - 10:36 am UTC

Thank goodness it FAILED!!!! It is your LUCKY DAY.

You are not using bind variables -- that is the hugest NO-NO in the world as far as oracle programming goes. You have the golden opportunity to correct this right now!

The error you have is that you included the ';' in the insert statement. ';' only means something to sqlplus (means -- finished typing, please run it). ';' is not part of the insert itself.

So -- recode the procedure to use bind variables!!!! and lose the ';'

Small Problem

Rahul, July 25, 2002 - 2:58 am UTC

Hi Tom
How r u ??? I'm fine and hope for the same...
one problem for you..

declare
v varchar2(100):='name,roll,class,marks'; --field names of a table
begin
open for cur 'select ' || v || ' ,city,pin from table';
loop
fetch...........
processing....
end loop;
close cur;

above is just sample not actual code..in which dynamic select is firing and variabke "v" contains few the field names and few are hardcoded.

my prob is that ...if we don't know that what fields, variable "v" will have next time then how can we fetch the values from dynamic cursor???????

Thanks.

Tom Kyte
July 25, 2002 - 6:47 am UTC

You must use the procedural method available via DBMS_SQL in this case. It lets you process a query where the number and types of the columns are not known until runtime.

You can search for dbms_sql on this site to see lots of examples, read the Supplied packages guide for the documentation -- search for dump_csv on this site to see an example that takes any query and processes it at runtime.

Sun Chen Hong Kong

Sun Chen, October 15, 2002 - 5:03 am UTC

HI, Tom
I write the stored procedure like the following to get value based on the column_name, but it get me the actually column name and does not the values of that columns I want!
How can I modify it? please give me some suggestions, thanks.

DECLARE
v_val_1 VARCHAR2(60);
v_val_2 number := 92;
BEGIN
FOR rec IN (SELECT column_name
FROM user_tab_columns
WHERE table_name = 'AC_PR'
AND column_name <> 'RUN_NO')
LOOP
execute immediate
'SELECT :X
FROM ac_pr
WHERE run_no = :Y'
INTO v_val_1
USING rec.column_name,
v_val_2 ;
DBMS_OUTPUT.PUT_LINE(v_val_1);
END LOOP;
END;
/

Tom Kyte
October 15, 2002 - 8:28 am UTC

first line of my answer tells you the problem:


....
Identifiers cannot be used as bind variables -- ever. Identifiers must be "hard
coded" into the query.
.......


loop
execute immediate
'select ' || rec.column_name || '
from ac_pr
where run_no = :y'
into v_val_1 using v_val_2;

......

Dynamic SQL Doubt

Abhijit, November 26, 2002 - 5:44 am UTC

Hi Tom,
The previous developer in my project has written the following stored procedure to execute dynamic sql.

CREATE OR REPLACE PROCEDURE EXEC_SQL(STRING IN VARCHAR2) AS
/* Local Variable Declaration Block */
CURSOR_NAME INTEGER;
RET INTEGER;
/* end of MICROSOFTDTPROPERTIES */

BEGIN -- Start Begin Block 1
CURSOR_NAME := DBMS_SQL.OPEN_CURSOR;
BEGIN -- Start Begin Block 2
DBMS_SQL.PARSE(CURSOR_NAME,
STRING, DBMS_SQL.native);
RET := DBMS_SQL.EXECUTE(CURSOR_NAME);
EXCEPTION -- Exception for Select Statement
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(CURSOR_NAME);
RAISE;
END -- End of Begin Block 2
; DBMS_SQL.CLOSE_CURSOR(CURSOR_NAME);
END; -- End of Procedure

I have written the following stored procedure for achieving the same result.

Create or replace procedure SP_ExecuteSQL(in_String IN varchar2)
Is
begin
Execute Immediate in_String;
Exception
When Others Then
dbms_output.put_line(SQLERRM);
end;


Could you please tell me what is the difference between the two?

Regards,
Abhijit

Tom Kyte
November 26, 2002 - 8:06 am UTC

Neither is as efficient as it could be and yours uses that nasty nasty SQLServer construct of predicating stored procedures with a pedantic "sp_" ;)

Yours also has a very very nasty bug!


when others then
... let us just silently ignore any and all errors ....


So, when the passed in statement fails -- what do you do? You ignore it. A when others that is not followed by a RAISE statement is almost always a BUG IN DISGUISE. In your case, it is most certainly a BUG.


I would SKIP the procedure all together and just code:


execute immediate p_the_string <using BIND VARIABLES>;


Neither of you are supporting bind variables in your dynamic sql and bind variables are the single most important thing you can learn. In SQLServer, given their "architecture" maybe binds were not important but in Oracle -- since you do want to scale up lots more -- you'll be wanting to use them.


So, erase the stored procedures.
Use execute immediate

and PLEASE use bind variables!!!

One More Doubt

Abhijit, December 02, 2002 - 8:30 am UTC

Hi Thomas,
Thanks for getting that across.
I have now dropped both of those stored procedures as advised by you.

I am using EXECUTE IMMEDIATE along with Bind Variables as follows.

sSQL = "EXECUTE IMMEDIATE " & _
"'Insert Into Poets(PID, FNAME, LNAME, COUNTRY) values(:w,:x,:y,:z)' " & _
" USING '" & txtPID.Text & "','" & txtFName.Text & "','" & _
txtLname.Text & "','" & txtCountry.Text & "';"

This SQL Statement is printed out as follows.
EXECUTE IMMEDIATE
'Insert Into Poets(PID, FNAME, LNAME, COUNTRY) values(:w,:x,:y,:z)' USING 'A7','Emily','Bronte','england'


My Poets Table is as follows
Create Table Poets
(
PID char(2),
FNAME varchar2(40),
LNAME varchar2(40),
COUNTRY varchar2(50)
)

Could you please tell me what am I doing wrong?

Cheers!
Abhijit



Tom Kyte
December 02, 2002 - 9:02 am UTC

whoa -- hold on there.


Are you writing code in VB or Java? Looks like VB.

Don't do that AT ALL then. Use a prepared statement, bind to it and execute it.


for example:

</code> http://groups.google.com/groups?selm=MPG.174376807c7a2b7a98968a%40news.t-online.de&output=gplain <code>

...

when in PLSQL and you are wanting to do dynamic sql -- use execute immediate.

Useful response

Abhijit, December 02, 2002 - 11:02 pm UTC

Hi Tom,
Thanks for your immediate response on EXECUTE IMMEDIATE.
I have figured out another way to do this apart from the link that you sent. Yes the code is in Visual Basic.

Here is my method.

sSQL = "BEGIN" & Chr(13)
sSQL = sSQL & "EXECUTE IMMEDIATE " & _
"'Insert Into Poets(PID, FNAME, LNAME, COUNTRY) values(:w,:x,:
y,:z)' " & _
" USING '" & txtPID.Text & "','" & txtFName.Text & "','" & _
txtLname.Text & "','" & txtCountry.Text & "';"
sSQL = sSQL & "END;"
mCn.Execute sSQL 'mCn is my connection object.

This works out as well. Is this binding variables?

Regards,
Abhijit





Tom Kyte
December 03, 2002 - 7:10 am UTC

That is NOT BINDING.

Please read the above referenced google link to see the correct and ONLY way you should be doing this.

If you do not see ? (question marks) in your statement -- you are not using bind variables. You are making a sql statement in a string with literals in it. You are doing it 100% wrong.

do not use a begin/end block with execute immediate.

Just use an INSERT -- period. Just like the above google link very clearly demonstrates!



Thanks Tom!

Abhijit, December 05, 2002 - 3:13 am UTC

That has cleared up my other doubts as well.

Cheers!
Abhijit

MS Visual C ++ 6.0 with ADO connection

Marcio, March 13, 2003 - 9:38 am UTC

Tom,
Hard to convince my developer colegue Visual-C that bind variable is a solution to improve performance. This is scenario: he has to call the SAME query about 100 times with distincts values on WHERE clause like 'select * from table_a WHERE b = 100' and 'select * from table_a WHERE b = 102' and so on.
I am thinking give up to convince him to use bind variable. He says there are many trouble to use this in his language and bla bla bla ...
I thought use stored procedure, but he refute saying "doesn't work here -- you don't know write any line on C program, it is different from your pl/sql and SQL [magic] *Plus ".

Tom, could you help with some piece of information?

Thanks alot!
Marcio

Tom Kyte
March 14, 2003 - 5:12 pm UTC

the programmer is a wuss -- plain and simple. A wuss.


send him here:
</code> http://asktom.oracle.com/~tkyte/runstats.html <code>

and use the last link on the page -- where after I explain how the test harness works, they'll see the results of such a test.

Similar Situation, would a procedure be good option?

Meyer, May 12, 2003 - 2:56 pm UTC

Tom,

I also have a person using VB and ADO that wants to inserts into my database. He set it up to use non binding SQL and I said no, especially since the insert on his side will be executed in a loop (1000 times per night into a wide table).

I can make him bind as in the google link or wondering if I created a procedure like the psudo code below. what effect on the shared pool and would you consider this binding before oracle? and a good or bad idea?

procedure insert_Row_from_bs_platform
(p1 varchar2, p2 varchar2)
is
begin
insert into table_name
(f1, f2) values
(to_date(p1,'mm/dd/yyyy HH:mi:ss am'), (p2) );
end;

I would put in a package and let him call that procedure (instead of the insert)...from ADO/VB w/text strings as inputs.

Would that be binding before the shared pool, how would this fit on your scale of doing things the "right" way?

Also wants to execute many that will fail for dup value...to let the few new records captured in (and avoid dups as there is no good ID in the other BS system)...should I force him to program a mechanism for the bulk of that on his side...or let the dup_val_on_index error in Oracle be his tool.

Other suggestions?

Thanks,
Meyer


Tom Kyte
May 12, 2003 - 3:00 pm UTC

it he does not bind the procedure call -- you are just as sunk.

Call them a nasty name and then make them AT LEAST code:

alter session set cursor_sharing=force;


before submitting any statements to your database.


For 1,000 rows, let him do it row by row if they want -- just make them use cursor sharing (even though BINDING is actually easier then not binding....)


Awesome...

Meyer, May 12, 2003 - 3:16 pm UTC

That makes sense...Thanks Tom.

Can I do dynamic lookups through dynamic sql?

A reader, May 12, 2003 - 5:03 pm UTC

Hi Tom,
Source tables data needs to be inserted into target tables (the definitions of target tables are same as source tables)after converting some of the input values (standard codes)to the lookup values. The lookup table contains table name(s) and column name(s)along with input value and lookup value. If the lookup value is found, substitute the input value with lookup value, insert row in target table else insert in reject table.
Thanks

Tom Kyte
May 12, 2003 - 7:04 pm UTC

you could but it would be slower then slow itself. generic is great, generic is dog slow for big data loads.

give me more details about the process itself. sounds like a "join" to me. sounds like you might want to build:


insert into target
select .....
from source, lkup1, lkup2, lkup3, ....
where (joins, outer joins if need be, to lookups)
/


use your generic table to build this SPECIFIC insert and execute it.

Here are more details...

A reader, May 13, 2003 - 5:02 am UTC

There are plenty of tables to be loaded with weekly run. The source tables (directly loaded from raw data)contain some code columnns +others with source_system_code. For example, for codes, employer table has employer status, employer type columns, and policy has policy type, policy category etc. The lookup table is built to standardise the codes, as data is coming from disparate systems. The lookup table contains source_system, table_name, column_name, date_effective from, date_effective_to, source code, standard code, columns. If for one system (SS1) the employer status 'A' means Active, other (SS2) may have 'R' (Recent) as Active. So, the table is built as
SS1, employer (table name), '01-JAN-2000','31-DEC-999', employer status (column name), A, C (current);
SS2,.........................,R, C. The target tables have referential integrity with standard codes tables with description like, table employer status table has status -> A, Description-> Active.
I was asking about building a generic script passing table name as an argument, get the lookup code columns, go to lookup table, return standard code, and finally insert into target the record with standard code. If the source code is not found then reject the record in rejects table.
Is it possible to do through views without the reject part? as I have a generic script which loads data in target, and puts records in rejects table if FK of codes tables is violated. In this way, I can use view as input to generic loading script. If possible, could you please give an example?
Thanks


Tom Kyte
May 13, 2003 - 7:27 am UTC

read my first response right above -- after seeing this, it just reaffirms what I would do.

It would be a single insert into statement.
It would be built at runtime.
It would involve no procedural code to move the data from the source to the target, only procedural code will be to build the insert into as select statement.

How to build lookups at run time...

A reader, May 15, 2003 - 4:28 am UTC

Tom,
Since I did't know all of the outputs at compile time, so I used DBMS_SQL to insert from source to target tables. The script works fine, but now the source tables columns having codes need to lookup in the lookup tables before insert into target tables. As DBMS_SQL's API's DEFINE_COLUMN and COLUMN_VALUE works with position, how to I lookup in lookup table i.e., how to determine how many lookup aliases (lkp1, lkp2....) are needed to dynamically passed table?

Tom Kyte
May 15, 2003 - 9:35 am UTC

as i SAID (over and over) just build an INSERT INTO as SELECT.

no lookups - JOIN -- databases do that well, they JOIN. it will do your lookup.

no code, just a query.

7. Dynamic Dynamic SQL 15 May 2003 18 May 2000 3 years old

Jigar Shah, May 15, 2003 - 11:08 am UTC

subject: i need to refer the value of the variable dynamically not thename March 06, 2002
Reviewer: nagaraju

*I thought it might be helpful to them...

declare
a number;
b varchar2(10) := 'c';
c varchar2(10) := 'XX';
d varchar2(10) := 'YY';
e varchar2(10);
abc varchar2(240);
begin
if a is null then
b := '''c''';
else
b := 'd';
end if;
abc := 'select ' || b || ' from dual ';
execute immediate abc into e;
dbms_output.put_line(e);
end;
/


Update where rownum<var_name

Preeti, May 15, 2003 - 1:30 pm UTC

Tom,
I have the following question about update query:

we have a loan table: state_cd,
user_id,
status_cd,
....

user_state_served: user_id,
state_cd,
last_asgnmt_dt

We want to distribute the loans equally to employees authorized to work in the state.
I do that by updating last_asgnmt_dt = sysdate and taking the user_id
with min(last_asgnmt_dt) for next assignment.

However, after the initial assignment the workload may be different between employees
based on number of loans processed in a state.
How do I take the average number of loans in a state and divide equally among employees

for example for the state of TX, there are 400 loans.
There 4 workers in that state. This the distribution right now:

emp 1: 150
emp2 : 50
emp3: 75
emp 4 : 0

unassigned: 125.

I want to equally divide all the active loans among 4 employees.

Tom Kyte
October 25, 2010 - 8:18 am UTC


Java Prepared statement and Java dynamic sql mutually exclusive ?

Alvin, May 30, 2003 - 4:05 am UTC

Greetings,

Is it possible to use prepared statements (PS)on an dynamic SQL generated by a front end web app ?

Our head Java programmer told me that only static queries can be PS'ed.

If PS'ed dynamic sql is possible can you provide a simple example ?

Thanks.

Tom Kyte
May 30, 2003 - 8:05 am UTC

there is no such thing as "static" queries in Java unless you are using SQLJ (and then you won't be using jdbc)...

You need a better HEAD java programmer. Of course, 100% of course, you can use prepared and even callable statements for dynamic sql generated by a front end application.

All sql in jdbc is dynamic sql.
There is no such thing as static sql in jdbc.

what a way to start the morning, coding java. oh well... so we have a web front end. It collects inputs from the user (from text fields whatever). It collects an array of "inputs". It builds a query based on that.

Say the user has a form like:


Ename Like: __________
Sal Greater Than: __________
Job Equal To: __________

and so on. They enter '%A%' in ename like, they enter 50 in Sal Greater than (hey, sort of like my advanced search...)

The application builds a query then:

select * from emp where ename like ? and sal > ?

and an array with "%A%", "50" in it. Then the code that cannot exist according to your developer would be:


import java.sql.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;

class dynsql
{

public static void run_my_query_properly( Connection conn,
String query,
String[] binds )
throws Exception
{
PreparedStatement pstmt;

pstmt = conn.prepareCall( query );
for( int i = 0; i < binds.length; i++ )
pstmt.setString(i+1,binds[i]);

ResultSet rset = pstmt.executeQuery();

while( rset.next() )
System.out.println( rset.getString(1) );

rset.close();
pstmt.close();
}



public static void main(String args[]) throws Exception
{

DriverManager.registerDriver
(new oracle.jdbc.driver.OracleDriver());

Connection conn =
DriverManager.getConnection
("jdbc:oracle:thin:@aria-dev:1541:ora817dev",
"scott",
"tiger");

String[] values = { "%A%", "50" };

run_my_query_properly( conn, "select * from emp where ename like ? and sal > ?", values );
}
}


While you might not be able to keep the ps open since the query changes (sort of like a ref cursor in plsql) in this case -- you CAN (and must -- must must must) use it so as to support BIND VARIABLES.


I wish, truly wish, jdbc didn't have statements at all -- #1 leading cause of really poor performance they are.

Thank you for taking the time and for being so generous in sharing !

Alvin, June 01, 2003 - 11:18 pm UTC

Respectfully yours,
Alvin

Dynamic SQL for private procedures

Fan, August 07, 2003 - 2:34 pm UTC

create or replace package test1 as
  procedure p_public1; 
  procedure p_public2; 
end;
/

create or replace package body test1 as
  procedure p_private as 
  begin
    null;
  end;

  procedure p_public1 as 
  begin
    p_private;
  end;

  procedure p_public2 as 
  begin
    execute immediate 'begin p_private; end;';
  end;
end;
/


SQL> exec test1.p_public1;

PL/SQL procedure successfully completed.

SQL> exec test1.p_public2;
BEGIN test1.p_public2; END;

*
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'P_PRIVATE' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
ORA-06512: at "PAGO_V20SUNP2.TEST1", line 14
ORA-06512: at line 1


How could I invoke a private procedure using Dynamic SQL?

Thanks. 

Tom Kyte
August 09, 2003 - 5:48 pm UTC

you cannot -- it is not in scope.

consider "execute immediate" to be just like a function -- defined in some other package. It is not "inline" -- it is a function call.


hence that private function is not visible.


Just use a CASE of if/then/else -- not only will it be much faster, use less resources -- but you KNOW you have to add code if you add a function anyway, you are already modifying that package.

Using IN for Prepared Statement.

Divakar, September 07, 2003 - 9:40 am UTC

Tom,

Let's assume that we have an input field that can accept multiple values from user.

Corresponding SQL statement would be

Select * from TABLENAME WHERE COL_VAL IN (val1,val2,val3)

How can we do this in Prepared statement?

Do we need to create three bind variables in statement?

Regards,
Divakar

Tom Kyte
September 07, 2003 - 6:17 pm UTC



if there is a reasonable fixed upper bound to the number of inlist items, you can use

where col_val in (?,?,....,?)

and bind N items (binding nulls when the user supplied no inputs)

if there is no reasonable upper limit, then
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:210612357425 <code>

is one method and a global temporary table would be yet another (bulk insert them and just use "where in (select * from gtt)"

check for data

A reader, October 09, 2003 - 6:54 am UTC

Hi

I amd trying to write a pl/sql to find invalid synonyms as follows

declare
x number;
begin
for i in (select synonym_name from user_synonyms)
loop
execute immediate 'select 1 from ' || i.synonym_name || ' where rownum = 1' into x;
if x is null then
dbms_output.put_line(i.synonym_name);
end if;
end loop;
end;
/

it seems like X is never null which cannot be true because I have invalid synonyms. How so? Why x is never null?

Tom Kyte
October 09, 2003 - 5:23 pm UTC

because, it is throwing an ORA 980 when the synonym is bad:

ops$tkyte@ORA920> create synonym totally_bogus for nothing_up_my_sleeve;

Synonym created.

ops$tkyte@ORA920> 
ops$tkyte@ORA920> declare
  2    x number;
  3  begin
  4     for i in (select synonym_name from user_synonyms)
  5     loop
  6           execute immediate 'select 1 from ' || i.synonym_name || ' where rownum = 1' into x;
  7           if x is null then
  8              dbms_output.put_line(i.synonym_name);
  9           end if;
 10     end loop;
 11  end;
 12  /
declare
*
ERROR at line 1:
ORA-00980: synonym translation is no longer valid
ORA-06512: at line 6


It never gets to the code "if x is null".  even if it did -- X would just have the LAST value of whatever was in it -- not null.

ops$tkyte@ORA920> create synonym totally_bogus for nothing_up_my_sleeve;

Synonym created.

ops$tkyte@ORA920> 
ops$tkyte@ORA920> declare
  2      synonym_bogus EXCEPTION;
  3          pragma exception_init( synonym_bogus, -980 );
  4  begin
  5     for i in (select synonym_name from user_synonyms)
  6     loop
  7     null;
  8               begin
  9                      execute immediate
 10                        'create or replace view v 
                              as select * from ' || i.synonym_name;
 11                   exception
 12                      when synonym_bogus then
 13                              dbms_output.put_line( i.synonym_name );
 14                   end;
 15     end loop;
 16  end;
 17  /
TOTALLY_BOGUS

PL/SQL procedure successfully completed.


is one technique.

 

x is never null

A reader, October 10, 2003 - 3:30 am UTC

tried this

set serverout on

select table_name from user_tables where table_name = 'EMP';

TABLE_NAME
------------------------------
EMP

select table_name from user_tables where table_name = 'MP';

no rows selected

declare
x number;
begin
for i in (select table_name from user_tables where table_name = 'MP')
loop
execute immediate 'select 1 from ' || i.table_name || '
where rownum = 1' into x;
if x is null then
dbms_output.put_line(i.table_name);
end if;
end loop;
end;
/

PL/SQL procedure successfully completed.

We should have seen table_name above

declare
x number;
begin
for i in (select table_name from user_tables where table_name = 'EMP')
loop
execute immediate 'select 1 from ' || i.table_name || '
where rownum = 1' into x;
if x is null then
dbms_output.put_line(i.table_name);
end if;
end loop;
end;
/

Expected behaviour

It seems that X is never null?

Tom Kyte
October 10, 2003 - 8:12 am UTC

I don't understand what your example is trying to show here.

The first loop never executes (there are no tables named MP). So, it is sort of "meaningless". none of the code in the loop was run.

In the second case, EMP did exist, the code DID run, X has the value of 1, 1 is NOT NULL, hence the body of the if statement was not executed.

expected behaviour all around.

Stored proc to build tables

A Reader, December 17, 2003 - 7:43 pm UTC

Tom:

Thanks much for all the help.

I have a question for you. The following code builds the table when it is called with no argument at all. Attempt to build the objects in other schemas failed but the proc successfully completed. What am I doing wrong here? Thanks.

CREATE OR REPLACE PROCEDURE BUILD_SCHEMA (p_schema_name IN VARCHAR2) AS

p_schema VARCHAR2(64);
p_sql VARCHAR2(2000);
results VARCHAR2(1000);

BEGIN

IF p_schema_name IS NOT NULL THEN
p_schema := p_schema_name || '.';
END IF;

p_sql := 'CREATE TABLE ' || p_schema || 'PERSONZ (
id NUMBER(*) PRIMARY KEY,
ts DATE NOT NULL,
last_name VARCHAR2(40) NOT NULL,
middle_name VARCHAR2(40),
first_name VARCHAR2(40) NOT NULL,
phone_num VARCHAR2(32) NOT NULL,
email_addr VARCHAR2(241),
street_addr_1 VARCHAR2(80),
street_addr_2 VARCHAR2(80),
city VARCHAR2(64),
state VARCHAR2(3),
zip VARCHAR2(16),
country VARCHAR2(3),
sel_security_ques VARCHAR2(32),
security_answer VARCHAR2(32)
) TABLESPACE xyz_ds';

EXECUTE IMMEDIATE p_sql INTO results;

COMMIT;

DBMS_OUTPUT.PUT_LINE(p_sql);
DBMS_OUTPUT.PUT_LINE(results);

EXECUTE IMMEDIATE 'CREATE SEQUENCE personz_seq';

EXCEPTION --Error handler
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE (results);
END;


Tom Kyte
December 18, 2003 - 10:46 am UTC

the procedure "completed successfully" because you used the EXCESSIVELY, extremely bad practice of a "when others" that is not followed by a RAISE statement.

you are hiding the errors here. The create table is failing, raising an exception and exiting the procedure. since p_results is NULL, you cannot "see" the error.


Couple of other points

a) the into results -- you are expecting what exactly to be returned??? results will ALWAYS be null in this procedure. the into results is meaningless.

b) DDL commits. The commit is "redundant"

c) this procedure can run once and then will fail on the create sequence every time afterwards (sequence already exists)

d) get RID of the exception block. THE ONLY time you want an exception block is when you are expecting some 'exception' to be thrown (like NO_DATA_FOUND for example, thats a common one) and you can DEAL WITH it (eg: it is not an error). Never never code an exception block like you have here.

Customize via anonymous PL/SQL blocks?

j., January 15, 2004 - 5:31 am UTC

we have to maintain data exchange via files between our oracle databases and customer systems.

several subsystems provide standardize interfaces to our internal systems for that purposes. they contain data structures for data to be received/sent as well as routines to process this data. incoming data is converted into the standardized internal formats via xml/xslt. on the output side xml/xsl serves for converting data into customer formats.

since these data structures reside on our internal systems that receive/send data they are *not* generic. but due to different customer needs we are forced to make the import routines more flexible (for export this is not a problem). for instance customers want to be able to exclude destination tables from beeing affected. in addition they want to configure whether rows should be imported (inserted/updated) as a whole or piecewice (process certain columns only).

to provide that scale of flexibility we are thinking of using dynamic SQL in conjunction with "normal" stored code: a stored PL/SQL routine implements the steps of the overall "workflow" - processing the data in the order it has to be done (due to target RI constraints).
every step uses its own anonymous PL/SQL block that gets intialized once per import and executed once per source row. each PL/SQL blocks contains a customized update and insert operation for a certain data structure. at runtime those PL/SQL blocks get invoked via cursors from within the main routine. in case of an update the rowid of the row to be updated is provided by the caller. the PL/SQL blocks request the data to be processed via a static callback function which reads one source row at a time (always the superset of "candidate" columns). the PL/SQL block in turn (in case it is called at all) processes the requested columns only. it handles errors so that the caller can request codes/messages in case the execution fails.

the described solution worked well. unfortunately it took almost double the time of the static PL/SQL solution.

question is: what do you think about this? do you see a better approach?

Tom Kyte
January 15, 2004 - 8:47 am UTC

when you did the dynamic sql -- you used dbms_sql right? to avoid a parse per execute.

or did you use native dynamic sql which in this case would be slower?

yes

j., January 15, 2004 - 9:49 am UTC

(i've read all your books ;o)

Tom Kyte
January 15, 2004 - 10:26 am UTC

last book (effective oracle) shows how much better static sql is than dynamic :)

you might just want to write a routine that dynamically generates the routines once at your customer site -- resulting in static, known sql/plsql code at each customer.

j., January 15, 2004 - 10:56 am UTC

but our (internal) customers run delphi and share the same database schemas ...

Tom Kyte
January 15, 2004 - 11:45 am UTC

huh? what does delphi have to do with plsql?

anyway -- guess I'd code for each "customer" and dynamically call a procedure per customer then.

j, January 16, 2004 - 10:25 am UTC

they use (thin) delphi clients to access the database. these clients call pl/sql code residing on server side.

static code doesn't seem to be solution flexible enough for our requirements (and preference to do as much as possible on database side :), since there are no static rules how the data has to be transferred from source to target. in fact the migration routines may even vary for different transfers of the exact same input format.

Tom Kyte
January 16, 2004 - 11:29 am UTC

so? I don't understand why delphi matters. it is just *a language*.




j., January 16, 2004 - 2:06 pm UTC

i just mentioned delphi because i thought you thought that there would be pl/sql available at client side when you wrote "write a routine that dynamically generates the routines once at your customer site -- resulting in static, known sql/plsql code at each customer."

our current scenario consists of one "universal" static pl/sql routine at database side. it determines the "frame" for invokation of several anonymous pl/sql blocks (one per DML target) generated at runtime according to customers needs for processing user specific customized DML operations (e.g. exclude or transform certain column values during DML).

i'm afraid i didn't understand your solution at all.
could you please explain it in a little more detail?

Tom Kyte
January 17, 2004 - 1:08 am UTC

guess you need a procedure per customer -- that is what I'm trying to get to -- did not know your customers were not running oracle.

why not generate (statically, via a code generator) a procedure for a customer and update the procedure if the customer profile changes.

j., January 17, 2004 - 5:10 am UTC

but then it 's all about how/where to store those generated routines. even if we would assign one dedicated schema per customer to hold these customized database objects we would run into problems with multiple sessions (transferring different data) for one customer at a time.

can you create a procedure by executing a procedure?

A reader, January 21, 2004 - 9:25 am UTC

Can you do this?

create or replace procedure wow
is
v_sql varchar2(1000);
begin

v_sql := 'create or replace procedure wowwow
is
begin
dbms_output.put_line(''Wowsers'');
end;
/
';
execute immediate (v_sql);
end;
/

After getting an ora-1031 insufficient privs on exec wow, I explicitly granted the 'create procedure' priv to my user, and re-executed.

Now I get:

eurokous@dev> exec wow
ERROR:
ORA-24344: success with compilation error
ORA-06512: at "eurokous.WOW", line 13
ORA-06512: at line 1

It's a problem with execute immediate. I'll read up, but what do you have to say?

Thanks much


Tom Kyte
January 21, 2004 - 9:38 am UTC

"/"

is a sqlplus thingy -- it tells sqlplus "please run the buffer, i'm done typing"

it is not part of sql.

lose the "/", that is the compilation error.

not a problem with execute immediate, rather problem with the code you pass to it.

Thanks!

A reader, January 21, 2004 - 12:08 pm UTC


OK

Richard, April 14, 2004 - 4:43 am UTC

Dear Tom,
When I do procedural SQL as

SQL>exec dbms_repcat_admin.do_sql('create table t(x int)');

 It affects the SYS schema.It is not stored in my catalog
but stored in SYS Schema.Why does this happen?
please do reply.

 

Tom Kyte
April 14, 2004 - 8:40 am UTC

why are you using replication tools to create a table?

just code:

execute immediate 'create table t(x int)';


in your plsql.

OK

Richard, April 15, 2004 - 2:51 am UTC

Hi Tom,
This procedure makes use of dynamic sql but it throws an error when called.

SQL>create or replace procedure var_p(x in number,y out varchar2) as
  2   c pls_integer default dbms_sql.open_cursor;
  3   n pls_integer;
  4   begin
  5    dbms_sql.parse(c,'Delete from empl where empno = :b1 returning ename into 

:b2',dbms_sql.native);
  6    dbms_sql.bind_variable(c,'b1',x);
  7    dbms_sql.bind_variable(c,'b2',y);
  8    n := dbms_sql.execute(c);
  9   dbms_sql.variable_value(c,':b2',y);
 10   dbms_sql.close_cursor(c);
 11  end;
SQL> /

Procedure created.

                                                                                

SQL> exec var_p(7902,:p)
BEGIN var_p(7902,:p); END;

*
ERROR at line 1:
ORA-03113: end-of-file on communication channel 


SQL> exec var_p(7934,:p)
BEGIN var_p(7934,:p); END;

*
ERROR at line 1:
ORA-03113: end-of-file on communication channel 


 

Tom Kyte
April 15, 2004 - 8:30 am UTC

ops$tkyte@ORA9IR2> create or replace procedure var_p(x in number,y out varchar2)
  2  as
  3  begin
  4          execute immediate
  5      'delete from emp where empno = :x returning ename into :y' using IN x, OUT y;
  6  end;
  7  /
 
Procedure created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> variable y varchar2(200)
ops$tkyte@ORA9IR2> exec var_p( 7900, :y )
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> print y
 
Y
------------------------------------------------------------------------
JAMES
 

OK

Richard, April 16, 2004 - 8:50 am UTC

Dear Tom,
If I have a table like
  SQL> create table t(id number,description varchar2(30));
I would like to insert MANY rows into this table through *PL/SQL.*
How to do it?
For example rows like
 SQL> insert into t values(1,'One');
 SQL> insert into t values(2,'Two');
 SQL> insert into t values(3,'Three');

need to be inserted through PL/SQL.
Could you please help?

 

Tom Kyte
April 16, 2004 - 9:16 am UTC

ops$tkyte@ORA9IR2> create table t ( id number, description varchar2(30) );
 
Table created.
 
ops$tkyte@ORA9IR2> begin
  2  insert into t
  3  select rownum, initcap(to_char(to_date(rownum,'j'),'jsp'))
  4    from all_objects
  5   where rownum <= 3;
  6  end;
  7  /
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2> select * from t;
 
        ID DESCRIPTION
---------- ------------------------------
         1 One
         2 Two
         3 Three
 

;)

but seriously -- not sure what you mean "through *PL/SQL.*"  

that seems to indicate to me that you are having a hard time getting started with plsql or something?  I mean - plsql is just a programming language, one that makes doing sql very easy.  do you have a specific issue? 

OK

Richard, April 16, 2004 - 11:36 am UTC

Dear Tom,
I meant,Can we make use of dbms_sql package to perform the
above mentioned operation USING bind arrays?I don't have any problem with pl/sql as you assume.

Tom Kyte
April 16, 2004 - 12:53 pm UTC

sure you can.

search this site for forall for lots of examples.

insufficient privileges error for dynamic SQL

Sean, July 20, 2004 - 12:06 pm UTC

Hi Tom,

I don't know why it gave me 'ORA-01031 insufficient privileges' error even for this simple dynamic sql.  I can do it in anonymous bloc, but not in procedure. I tried in system user, but got the same error.  Here is the SQL.  

Thanks so much for your help.

----------------------------------------------------------
SQL> BEGIN
  2  execute immediate 'create table t(x int)';
  3  END;
  4  /

PL/SQL procedure successfully completed.

SQL> drop table t;

Table dropped.

SQL> CREATE OR REPLACE PROCEDURE add_table IS
  2  BEGIN
  3  execute immediate 'create table t(x int)';
  4  END;
  5  /

Procedure created.

SQL> exec add_table
BEGIN add_table; END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SCOTT.ADD_TABLE", line 3
ORA-06512: at line 1
-----------------------------------------------------
 

Tom Kyte
July 20, 2004 - 8:30 pm UTC

Need 'create table priviledge' for dynamic SQL

Sean, July 20, 2004 - 8:52 pm UTC

Hi Tom,

I learned so much from you, but I will always remember thie one.
-------------------------
SQL> connect system/password
Connected.
SQL> grant dba to scott;

Grant succeeded.

SQL> connect scott/tiger
Connected.
SQL> CREATE OR REPLACE PROCEDURE add_table IS
  2  BEGIN
  3     execute immediate 'create table t(x int)';
  4  END;
  5  /

Procedure created.

SQL> exec add_table
BEGIN add_table; END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SCOTT.ADD_TABLE", line 3
ORA-06512: at line 1


SQL> connect system/password
Connected.
SQL> grant create procedure to scott;

Grant succeeded.

SQL> connect scott/tiger
Connected.
SQL> CREATE OR REPLACE PROCEDURE add_table IS
  2  BEGIN
  3     execute immediate 'create table t(x int)';
  4  END;
  5  /

Procedure created.

SQL> exec add_table
BEGIN add_table; END;

*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "SCOTT.ADD_TABLE", line 3
ORA-06512: at line 1


SQL> set role none;

Role set.

SQL> create table t(x int);
create table t(x int)
*
ERROR at line 1:
ORA-01031: insufficient privileges


SQL> connect system/password
Connected.

SQL> grant create table to scott;

Grant succeeded.

SQL> connect scott/tiger
Connected.
SQL> set role none;

Role set.

SQL> create table t(x int);

Table created.

SQL> drop table t;

Table dropped.

SQL> CREATE OR REPLACE PROCEDURE add_table IS
  2  BEGIN
  3     execute immediate 'create table t(x int)';
  4  END;
  5  /

Procedure created.

SQL> exec add_table

PL/SQL procedure successfully completed.
------------------------------------------ 

Dynamically invoking private procedures

A reader, August 09, 2004 - 2:08 pm UTC

I have a package with ONE exposed procedure that accepts a parameter and executes a local (not in the package spec) procedure to do stuff.

Instead of validating the parameter and running the local procedure the traditional way, is there a easier way to do this in one go using the new associative arrays in 9iR2?

i.e. if I setup a assoc array like

proc_name('parameter1') := 'proc1';
proc_name('parameter2') := 'proc2';
...

Now, my main(), so to speak, has to simply do (in theory)

procedure process(p_parameter in varchar2)
is
begin
if proc_name.exists(p_parameter)
then
execute proc_name(p_parameter);
end if;
end;

Sort of like the 'eval' command in Unix shell scripts!

Of course, this doesnt work.

[I just thought that doing this versus a huge if/then/elsif statement is more elegant]

Any ideas? Thanks

Tom Kyte
August 09, 2004 - 2:35 pm UTC

execute immediate necessitates a "scope change" (think of execute immediate as if it were a subroutine in another package).

this scope changes makes all of your local procedures "disappear"

so, they would have to be exposed.


You can put the "private" routines into yet another package, expose them all via the spec and just not "grant" on that package. So, you achieve the same goal.


But if you are doing this "lots" the overhead of dynamic sql would be far too much. I would use a CASE statement.

Thanks

A reader, August 09, 2004 - 2:39 pm UTC

I am not doing this lots so the overhead of dynamic SQL is acceptable for me.

Is there a way I can expose all the private procedures in the package spec but make them not really public? i.e. put some code to prevent direct execution of those procs? Only a designated "wrapper" proc (also public) is allowed to call these procs?

Thanks

Tom Kyte
August 09, 2004 - 4:10 pm UTC

you can use who_called_me in the second package I said to create (the one that will not be granted to others) </code> http://asktom.oracle.com/~tkyte/ <code> this package could check to make sure it was being called by this first wrapper package

Dynamic SQL vs. Java

j., August 12, 2004 - 6:20 am UTC

we want to do some calculation within the database - based on different formulas that reference certain database entries, e.g.:

result := [placeholder1] + [placeholder2]

each formula is evaluated multiple times with changing input values for its placeholders (extracted from the database by using PL/SQL).

from our current point of view there are at least 3 options available:

1st: using dynamic SQL
prepare the formula by replacing their placeholders with appropriate SYS_Context-entries and opening a cursor (parse once, execute many)

1a: generate a PL/SQL-block, that calculates the result and invokes a procedure to store the result in a package variable accessible to the caller.

1b: generate a SQL-statement instead of a PL/SQL-block and receive the result via fetch

2nd: using JAVA
generate the result by invoking a (generated) parser, that evaluates the given expression

the 1st approach would introduce context-switches between PL/SQL and SQL, the 2nd one between PL/SQL and JAVA.

what option would you start prototyping with in order to compare/measure?

Tom Kyte
August 12, 2004 - 9:19 am UTC

I'd need more context -- for example if these functions are applied to a "row", i would change the routine that "saves" these formulas to create me a nice view that incorporates them for example.

so, i'd say "insufficient data"


but it might be nice to store the formula's as a view

create view my_formula_name
as
select sys_context( 'formula', 'placeholder1' ) + sys_context( 'formula', 'placeholder2' ) from dual;



and your "eval" routine would take a correlated array of names/values and a formula name, populate the context and 'select * from ' || my_formula_name



j., August 12, 2004 - 11:31 am UTC

formulas are to be defined by the users at runtime.

calculated results and input values (for placeholders) are located within one table - but never within one row. formulas are stored outside that table but get assigned to certain entries of that table.

let me give you a simplified example:

id1 id2 value
1 'A' 30 <-- calculated: ['B'] + ['C'] for id1 = 1
1 'B' 10
1 'C' 20
2 'A' 70 <-- calculated: ['B'] + ['C'] for id1 = 2
2 'B' 35
2 'C' 35

here formula ['B'] + ['C'] is assigned to entries with id2 = 'A' and references entries with id2 in ('B', 'C'). by definition formulas always apply to entries with the same id1. id1 determines some kind of 'scope' for looking up the input values for the calculation.

what of our 3 approaches would you prefer (to check out first)?

Tom Kyte
August 12, 2004 - 11:39 am UTC

yes - and until then?

j., August 12, 2004 - 12:29 pm UTC

i guess we have to check out ourselves what is more/less expensive:

... to do a lot of string processing to replace placeholders with values within the formulas in order to feed the parser (that 's doesn't perform any database call) with its input string ...

... or to use those context-entries to provide input to an anonymous PL/SQL-block or SQL-statement for calculation ...

which context-switch is more efficient: between PL/SQL and JAVA or PL/SQL and SQL? should one prefer the SQL-statement or is an anonymous PL/SQL-block the better choice?

Tom Kyte
August 12, 2004 - 12:38 pm UTC

this is something I would not do in the database itself, but rather in the client application. You are building a spreadsheet. I'd let the thing that displays the data do this (heck, I might just return a "spreadsheet" and let the spreadsheet do what it does best).

the model clause was called spreadsheet in the beta actually, it is a spreadsheet in the database.

j., August 12, 2004 - 2:25 pm UTC

sure, i agree in general, but we 've two reasons to prefer the database side:

1st: this "spreadsheet"-thing is just a small part in addition to an application, which is implemented at the database (and we don't want to spread it ;o)

2nd: we 've to minimize the need to transfer input data over the network just to calculate a result, that gets stored back into the same database.

so our questions remain after all: choose JAVA or dynamic SQL or "dynamic PL/SQL"?

Tom Kyte
August 12, 2004 - 3:25 pm UTC

middle tier application -- you won't get me to say "here is an awesome way to do this in the database" -- i don't believe there is one in 9i and before. You have to load up this big thing -- dynamically process lots of junk -- sounds like something I want to do programatically away from the database.

10g- future of sql?

chavan, August 12, 2004 - 3:34 pm UTC

Based on your requirements, I'd seriously consider 10g and the model.

</code> http://www.oreillynet.com/pub/a/network/2004/08/10/MODELclause.html <code>


OK

Raju, August 13, 2004 - 3:34 am UTC

Dear Sir,
I tried this procedure but it throws some errors.What is wrong with this?
Please do reply.

SQL>create or replace procedure psql3(jb in emp.job%type,salaries out dbms_sql.number_table)
  2  authid definer
  3  as
  4  begin
  5   execute immediate 'update emp set sal = sal * 1.2 where job = :x returning sal
  6                       bulk collect into :sals' using IN jb,OUT salaries;
  7* end;
SQL> /

Warning: Procedure created with compilation errors.

SQL> show err
Errors for PROCEDURE PSQL3:

LINE/COL ERROR
-------- -----------------------------------------------------------------
5/2      PL/SQL: Statement ignored
6/63     PLS-00457: expressions have to be of SQL types
 

Tom Kyte
August 13, 2004 - 10:12 am UTC

why would you use dynamic sql there?

native dynamic sql only supports sql types -- "create type numArray as table of number"

dbms_sql.number_table is a plsql type. not appropriate


but here, you should not, would not use dynamic sql.

Help

Raju, August 16, 2004 - 2:01 am UTC

Dear Tom,
Thanks for your reply.Could you please have a look at the code below?

SQL>  create or replace procedure p2(dno in number,enms out       vc2arr)
  2  authid current_user
  3  as
  4  begin
  5  execute immediate 'update e set sal = sal * 1.2 where deptno = :deptno returning
  6                     ename into :ename' using IN dno,OUT enms;
  7* end;
SQL> /

Procedure created.

SQL> desc vc2arr
 vc2arr TABLE OF VARCHAR2(30)

SQL> declare
  2  l_names vc2arr;
  3  begin
  4  p2(20,l_names); -- Procedure call
  5  for i in 1..l_names.count loop
  6   dbms_output.put_line(l_names(i));
  7  end loop;
  8  end;
  9  /
declare
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected SCOTT.VC2ARR got CHAR
ORA-06512: at "SCOTT.P2", line 5
ORA-06512: at line 4

How to correct this?
Please do reply.
Bye!
 

Tom Kyte
August 16, 2004 - 8:28 am UTC

<b>don't use dynamic sql unless you have to and here you do not have to</b>

ops$tkyte@ORA9IR2> @test
ops$tkyte@ORA9IR2> create or replace type vc2arr as table of varchar2(30)
  2  /
 
Type created.
 
ops$tkyte@ORA9IR2> drop table emp;
 
Table dropped.
 
ops$tkyte@ORA9IR2> create table emp as select * from scott.emp;
 
Table created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create or replace procedure p2(dno in number,enms out            vc2arr)
  2  authid current_user
  3  as
  4  begin
  5  execute immediate '
  6  begin
  7    update emp set sal = sal * 1.2 where deptno = :deptno returning ename bulk collect into :ename;
  8  end;' using IN dno,OUT enms;
  9  end;
 10  /
 
Procedure created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> declare
  2  l_names vc2arr;
  3  begin
  4  p2(20,l_names); -- Procedure call
  5  for i in 1..l_names.count loop
  6   dbms_output.put_line(l_names(i));
  7  end loop;
  8  end;
  9  /
SMITH
JONES
SCOTT
ADAMS
FORD
 
PL/SQL procedure successfully completed.
 

Dont get it

A reader, August 16, 2004 - 3:11 pm UTC

I dont get it, what is the difference between what you did

execute immediate '
begin
update emp set sal = sal * 1.2 where deptno = :deptno returning ename bulk collect into :ename;
end;' using IN dno,OUT enms;

and what the person was doing

execute immediate '
update e set sal = sal * 1.2 where deptno = :deptno returning ename into :ename' using IN dno,OUT enms;

He was simply missing the BULK COLLECT keyword in the RETURNING clause, right?

Why did you enclose your update in a anonymous PL/SQL block?

Thanks

Tom Kyte
August 16, 2004 - 7:56 pm UTC

i did a block that used "static" sql *(as far as the dynamic block was concerned)* so I could use the returning ename BULK COLLECT clause.

did you trying to fix their code before asking to see if what you suggest would work?

How to handle dynamic parameters and bind variables in the dynamic sql

Muhammad Ibrahim, August 16, 2004 - 9:21 pm UTC

Dear Tom,

I have some questions regarding dynamic sqls.

Goal is to achieve calling procedure or function dynamically with or without
parameters passed.

I want to call procedure/function depends on the method_id i receive.
If it is just procedure/functions without any parameters then i can
do it by dynamic sql. How to change the parameters dynamically depends
on the method?

-- Here Method means not java mothed. Just a procedure/function.

eg: In the below table all the methods with procedure name to be called are
stored. In the Process_Simple_Case procedure i receive the method_id so
i can fetch the procedure/function name from the table by method_id. My
question is these procedures/functions might have parameters.In this case
how i can call that procedure dynamically?

Create Table Define_Methods ( Country_Code Varchar2(10),
Method_Id Number(10),
Method_Name Varchar2(200) );

Insert Into Define_Methods ( Country_Code, Method_Id, Method_Name )
Values ( 'IND', 1, 'My_Package.Process1' );

Insert Into Define_Methods ( Country_Code, Method_Id, Method_Name )
Values ( 'IND', 2, 'My_Package.Process2' );

Insert Into Define_Methods ( Country_Code, Method_Id, Method_Name )
Values ( 'US', 1, 'My_Package.Process5' );

Insert Into Define_Methods ( Country_Code, Method_Id, Method_Name )
Values ( 'US', 5, 'My_Package.Process1' );

-- Methods

Create Or Replace Package My_Package Is

Procedure Process1( pStart_Date In Date,
pSeq_Id In Number );
Procedure Process2( pStart_Date In Date,
pEnd_Date In Date );
Procedure Process3( pSeq_Id In Number );
Procedure Process4;
Procedure Process5( pStart_Date In Date,
pEnd_Date In Date,
pSeq_Id In Number,
pDescription In Varchar2 );
End My_Package;


Create Or Replace Package Body My_Package Is

Procedure Process1( pStart_Date In Date,
pSeq_Id In Number )
Is
Begin
-- Some processing
Null;
End Process1;

Procedure Process2( pStart_Date In Date,
pEnd_Date In Date )
Is
Begin
-- Some processing
Null;
End Process2;

Procedure Process3( pSeq_Id In Number )
Is
Begin
-- Some processing
Null;
End Process3;

Procedure Process4
Is
Begin
-- Some processing
Null;
End Process4;

Procedure Process5( pStart_Date In Date,
pEnd_Date In Date,
pSeq_Id In Number,
pDescription In Varchar2 )
Is
Begin
-- Some processing
Null;
End Process5;

End My_Package;

Procedure Process_Simple_Case ( pMethod_Id In Number,
pStart_Date In Date,
pEnd_Date In Date,
pSeq_Id In Number,
pDescription In Varchar2 )
Is
Begin


Select Method_Name
Into lMethod_Name
Where Method_Id = pMethod_Id;

-- lMethod_Name can be function or procedure . But in this
-- example carries only procedure

Execute Immediate( 'Begin lMethod_Name(...parameters may change for each method) End;' Using pStart_Date, pEnd_Date....);

-- Bind variables after "Using" clause varies depends on the
-- no. of parameters passed.But the parameters will
-- be within the Process_Simple_Case parameters receiving.

End Process_Simple_Case;

Is it possible to achieve this? If not can you suggest any other way to do this?

Thanks and Regards,
Ibrahim.


Tom Kyte
August 17, 2004 - 7:23 am UTC

the easiest way in your case, given your example would be to build a block like this:

l_block :=
'declare
l_start_date date := :x1;
l_end_date date := :x2;
l_seq_id number := :x3;
l_desc long := :x4;
begin
' || ...... passing l_ parameters || ';
end;';

execute immediate l_block using pStart_date, p_End_date, pSeq_id, pDescription;

You just have to figure out how you want to figure out what args to pass there, user_arguments or dbms_describe could be helpful if you want to do it 100% dynamically, easiest of course would be to insert:

'My_Package.Process1( l_start_date, l_seq_id );'

instead of just the name.



Using clause

Muhammad Ibrahim, August 18, 2004 - 4:43 am UTC

Dear Tom,
Thanks for your inputs. I am using User_Arguments to make it 100% dynamic. I have a small question.

1) In your below example is it possible to send the parameters as a string after using clause.

eg:

lall_parameters varchar2(4000) := 'pStart_date, p_End_date, pSeq_id, pDescription';

l_block :=
'declare
l_start_date date := :x1;
l_end_date date := :x2;
l_seq_id number := :x3;
l_desc long := :x4;
begin
' || ...... passing l_ parameters || ';
end;';


execute immediate l_block using pStart_date, p_End_date, pSeq_id, pDescription;

Instead :

execute immediate l_block using lall_parameters;


Thanks and Regards,
Ibrahim.

Tom Kyte
August 18, 2004 - 8:23 am UTC

no, it would not make sense, you are passing the inputs to execute immediate -- you happen to need to send 4 inputs, 4 separate variables. you would not be passing it a "single string", you are passing two dates, a number and a string.

How to pass the inputs to execute immediate in this scenario???

Muhammad Ibrahim, August 18, 2004 - 11:42 pm UTC

Dear Tom,

Thanks for your reply. Below is my situation. Please help me with your valuable inputs. Thanks in advance.

Create Table Define_Methods ( Method_Id Number, Package_Name Varchar2(30), Object_Name Varchar2(30), Overload Varchar2(40) );

Insert Into Define_Methods Values ( 1, MY_PACKAGE, PROCESS1, Null );
Insert Into Define_Methods Values ( 2, MY_PACKAGE, PROCESS2, Null );
Insert Into Define_Methods Values ( 2, MY_PACKAGE, PROCESS5, Null );

-- Below PLSQL table is to hold the values for the using clause.
Type rParam_List Is Record ( pMode Varchar2(10),
pStart_Date Date,
pEnd_Date Date,
pSeq_Id Number,
pTotal_Amount Number,

pCode Varchar2(10),
pDescription Varchar2(200),
pAmount Number );
Type tParam_List Is Table Of rParam_List;

-- To get all the Parameters for the dynamic procedure/function.
Function Get_Method_Arguments ( pMethod_Id In Define_Methods.Method_Id%Type )
Return Method_Arguments Pipelined Is

lOverLoad Define_Methods.OverLoad%Type Default Null;
lPackage_Name Define_Methods.Package_Name%Type;
lObject_Name Define_Methods.Object_Name%Type;
lMethod_Arguments Method_Arguments_Rec := Method_Arguments_Rec( Null, Null, Null, Null ); -- SQL Type
Begin
Select Package_Name, Object_Name, OverLoad
Into lPackage_Name, lObject_Name, lOverLoad
From Define_Methods
Where Method_Id = pMethod_Id;

For rMethod_Args In ( Select Argument_Name, Position, Pls_Type, In_Out
From User_Arguments
Where Package_Name = lPackage_Name
And Object_Name = lObject_Name
And Nvl( lOverLoad, '-1' ) = Nvl( lOverLoad, '-1' )
Order By Position )
Loop
lMethod_Arguments.Argument_Name := rMethod_Args.Argument_Name;
lMethod_Arguments.Position := rMethod_Args.Position;
lMethod_Arguments.Pls_Type := rMethod_Args.Pls_Type;
lMethod_Arguments.In_Out := rMethod_Args.In_Out;
Pipe Row( lMethod_Arguments );
End Loop;
Return;
End Get_Method_Arguments;

-- Actual processing
Function Process_Simple_Case ( pMode In Varchar2,
pStart_Date In Date,
pEnd_Date In Date,
pSeq_Id In Number,
pTotal_Amount In Out Number )

Return Pls_Integer Is

lParam_List tParam_List;
lBlock Varchar2(1000);
lParameters Varchar2(2000);
lBind_Variables Varchar2(2000);
lPackage_Name Define_Methods.Package_Name%Type;
lObject_Name Define_Methods.Object_Name%Type;
Begin

For Rec In ( Select Code, Description, Amount, Method_Id
From Customer_Table )
Loop
lParam_List := tParam_List();

-- Construct the bind variables to be passed for the dynamic sql
lParam_List.Extend();

-- the below holds the parameters from the Process_Simple_Case function
lParam_List(1).pMode := pMode;
lParam_List(1).pStart_Date := pStart_Date;
lParam_List(1).pEnd_Date := pEnd_Date;
lParam_List(1).pSeq_Id := pSeq_Id;
lParam_List(1).pTotal_Amount := pTotal_Amount;

-- the below holds the Local Cursor variables/if any local variable defined.
-- this PLSQL is to achieve all the Using values to be kept so that it
-- doesnt from where it comes from or goes out. But this PLSQL table should
-- always have a single record in it.

lParam_List(1).pCode := Rec.Code;
lParam_List(1).pDescription := Rec.Description;
lParam_List(1).pAmount := Rec.Amount;


-- Construct the dynamice parameters and values after using clause
-- for the procedure/function to be called!
For rMethod_Arguments In ( Select Argument_Name, Position, Pls_Type, In_Out
From Table( Get_Method_Arguments ( Rec.Method_Id ) )
)
Loop
-- here i need to construct lParameters and lBind_Variables
lParameters := ???; -- this parameter we can get it
-- from the cursor and concatenate them


lBind_Variables := ???; -- how to do this?
-- Values should be bind parallel to the
-- lParameters.
-- All the parameters for the lParameters list
-- varies depends on the
-- cursor Argument list and also Bind Variables
-- which comes from the
-- PLSQL table also varry accordingly.
End Loop;

Select Package_Name, Object_Name
Into lPackage_Name, lObject_Name
From Prl_Ri_Available_Methods_Ref
Where Method_Id = Rec.Method_Id;

lBlock := 'Begin '||lPackage_Name||'.'||lObject_Name||'( '||lParameters||' ); End;';

Execute Immediate lBlock Using lBind_Variables;

End Loop;
Return;
End Process_Simple_Case;

Regards,
Ibrahim.

Tom Kyte
August 18, 2004 - 11:47 pm UTC

sorry, no clue as to what your goal or objective here is.

I told you how to do this above

execute immediate lblock using variable1, variable2, variable3

and so on. We intelligently crafted this block so that it takes a FIXED NUMBER OF INPUTS for this very very reason

Dynamic Sql to call procedure/function - Using values

Muhammad Ibrahim, August 19, 2004 - 4:39 am UTC

Dear Tom,
May be this simple example gives you clear picture.

Procedure Process_Simple_Case( pStart_Date In Date,
pEnd_Date In Date,
pSeq_Id In Number,
pCode In Varchar2 )
Is

lPackage_Name Varchar2(50) := 'My_Package';
lObject_Name Varchar2(50) := 'Process1';
lParamaeters Varchar2(50);
Begin
lParamaeters := ':pStart_Date, :pEnd_Date, :pSeq_Id';
lBlock := 'Begin '||lPackage_Name||'.'||lObject_Name||'( '||lParameters||'); End;';

--So the variable lBlock will look like this
lBlock := 'Begin My_Package.Process1( :pStart_Date, :pEnd_Date, :pSeq_Id ); End;';

Execute Immediate lBlock
Using lStart_Date, lEnd_Date, lCode;

This is fine but my problem is here it is Process1 it might be Process2/Process3 etc..
So the values after Using differs according to the Process. I understand fully we cannt dynamically
create the list for the Using clause values So how i can acheive this scenario? is it possible?

eg:

1) Execute Immediate 'Begin My_Package.Process1( :pStart_Date, :pEnd_Date, :pSeq_Id ); End;'
Using lStart_Date, lEnd_Date, lCode;

2) Execute Immediate 'Begin My_Package.Process2( :pSeq_Id, :pCode ); End;'
Using lSeq_Id, lCode;

and so on processes.

I know the parameter and the values but i need to pass the values only depends on the
what ever parameter goes in.

something like this:

lBlock := 'Begin '||lPackage_Name||'.'||lObject_Name||'( '||lParameters||'); End;';

Execute Immediate lBlock
Using ...

Depends on the parameters for the process which is contructed inside the lBlock the below values for Using clause should be passed.

After using clause is it possible to change the values dynamically?

End;

Thanks a lot Tom.

Regards,
Ibrahim.


Tom Kyte
August 19, 2004 - 9:45 am UTC

please read above for my solution to that.

l_block :=
'declare
l_start_date date := :x1;
l_end_date date := :x2;
l_seq_id number := :x3;
l_desc long := :x4;
begin
' || ...... passing l_ parameters || ';
end;';

execute immediate l_block using pStart_date, p_End_date, pSeq_id, pDescription;


ALLL blocks would take ALLL four inputs -- period. QED

Nice

Siva, August 20, 2004 - 2:37 am UTC

Dear Sir,
This anonymous block hangs forever.I think the problem is due
to the execute_and_fetch procedure of dbms_sql package.How to
fix this?

SQL> declare
    2  v_deptno dept.deptno%type;
    3  c pls_integer default dbms_sql.open_cursor;
    4  begin
    5  dbms_sql.parse(c,'select deptno from dept',dbms_sql.native);
    6  dbms_sql.define_column(c,1,v_deptno);
    7  loop
    8  if dbms_sql.execute_and_fetch(c) = 0 then
    9  exit;
   10  end if;
   11  dbms_sql.column_value(c,1,v_deptno);
   12  dbms_output.put_line(v_deptno);
   13  end loop;
   14  dbms_sql.close_cursor(c);
   15  exception
   16  when others then
   17  if dbms_sql.is_open(c) then
   18   dbms_sql.close_cursor(c);
   19  end if;
   20*  end;
  
How to use the execute_and_fetch procedure?Could you please help?
Please do reply.
Bye!
 

Tom Kyte
August 20, 2004 - 10:51 am UTC

you are executing the query over and over -- as long as the table has at least ONE row -- execute and fetch will succeed forever like that.

you want to execute once
then fetch over and over

execute and fetch is useful to emulate "select into" (when you want a single row).

don't use it for mult-row results like this code:


open
parse
execute
loop
fetch
exit when done
end




OK

Jason, August 20, 2004 - 1:20 pm UTC

Hi Tom,
Using dbms_sql package,Is it possible to fetch an entire row
rather tahn fetching individual column values?I tried this procedure but I find it difficult to complete it.Could you
please tell me how to proceed?The procedure is as follows.

SQL> create or replace procedure proc(enm in emp.ename%type default 'BLAKE')
as
c pls_integer default dbms_sql.open_cursor;
begin
dbms_sql.parse(c,'select * from emp where ename = :ename',dbms_sql.native);
dbms_sql.bind_variable(c,':ename',enm);

How to fetch an entire row in dbms_sql?Can here the procedure
execute_and_fetch be used?

Please do reply.

 

Tom Kyte
August 21, 2004 - 11:03 am UTC

when using dbms_sql, you fetch the entire row but you have to procedurally "get the column value" for each column therein. it cannot work any other way since plsql doesn't have the concept of a pointer.

execute and fetch is designed to reduce round trips, nothing more, nothing less.

Insufficient privs when executing dynamic sql

Thiru, August 31, 2004 - 1:09 pm UTC

Is there anything that I am missing :

I have a stored proc wherein I insert into a global temp table as select * from my_tbl. This operation takes 1 sec ( for 30000 rows) according to tkrprof output. I thought of creating a table and an index with nologging dynamically.

create or replace procedure gt1
as
begin
execute immediate 'create table gt1 nologging as select * from my_tbl ';
end;
/

When executing this procedure, I get insufficient privileges. Same users' table being accessed.

Tom Kyte
August 31, 2004 - 1:41 pm UTC

don't think that way. use a global temporary table. this is not an appropriate approach in oracle. doing ddl is a really bad idea. if I were your dba, there would be strict rules about it (eg: you would have to work really hard to convince me with science that it is necessary)

</code> http://asktom.oracle.com/Misc/RolesAndProcedures.html <code>

Thiru, August 31, 2004 - 2:09 pm UTC

Actually I meant to use global temp table in the execute immediate script. When running from sqlplus and it runs fine in no time compared to the insert of 1 sec that creates 430708 redo size.
begin
execute immediate 'create global temporary table gt6 on commit preserve rows as select * from t5 ';
end;
/
Elapsed: 00:00:00.00

So in your opinion, this is ok of creating global temp tbl through the stored proc. The whole idea here is to quicken the data insert into a temp table for further processing.

Tom Kyte
August 31, 2004 - 2:48 pm UTC

no, it is my learned experience that this would be a horrible idea.

doing ddl in a stored procedure is not a good idea at all.


lets see the tkprof showing the insert taking 1 second vs CTAS taking 0.00

not that I don't believe you -- but... 0.00 is sort of SHORT for a CTAS without any data.



ops$tkyte@ORA9IR2> drop table gt6;
 
Table dropped.
 
Elapsed: 00:00:00.03
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> begin
  2    execute immediate 'create global temporary table gt6 on commit preserve rows as
  3    select * from big_table.big_table where rownum < 40000 ';
  4  end;
  5  /
 
PL/SQL procedure successfully completed.
 
Elapsed: 00:00:00.38
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> truncate table gt6;
 
Table truncated.
 
Elapsed: 00:00:00.02
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> insert into gt6 select * from big_table.big_table where rownum <40000;
 
39999 rows created.
 
Elapsed: 00:00:00.18



and when I run tkprof I'm not seeing any "1 second vs 0 seconds" 

Thiru, August 31, 2004 - 2:34 pm UTC

In the following procedure my_tbl belongs to THIRU and the procedure global_temp_sp is created in THIRU. So I did not understand why insufficient privs error.

SQL> create or replace procedure global_temp_sp
  2  as
  3  begin
  4  execute  immediate 'create global temporary table gt3  as select * from my_tbl';
  5  end;
  6  /

Procedure created.

Elapsed: 00:00:00.00
SQL> begin
  2  global_temp_sp;
  3  end;
  4  /
begin
*
ERROR at line 1:
ORA-01031: insufficient privileges
ORA-06512: at "THIRU.GLOBAL_TEMP_SP", line 4
ORA-06512: at line 2

Whereas if I do like this, there is no error:

SQL> begin
  2  execute  immediate 'create global temporary table gt3  as select * my_tbl ';
  3  end;
  4  /

PL/SQL procedure successfully completed.

Elapsed: 00:00:00.00

Thanks for the time. 

Tom Kyte
August 31, 2004 - 3:01 pm UTC

did you read the above referenced link i gave you?

(and this would be a horrible, bad, not good (tm) idea -- as stated, I would not permit this in my database)


dinamic refresh

Eugene, September 08, 2004 - 8:04 pm UTC

Hi Tom,
At the end of the procedure run, I want to be able to refresh the table on another server.
Here is what works in SQL*Plus:
Execute dbms_snapshot.refresh ('"UB_OBJECTS"."BO_SORD_SUMMARY"', 'c');
Here is what I am trying to do dinamically:
SELECT ''''||'dbms_snapshot.refresh'||'('||''''||'"'||'UB_OBJECTS'||'"'||'.'||'"'||'BO_SORD_SUMMARY'||'"'||''''||','||''''||'c'||''''||')'||''''
INTO vRep_string
FROM dual;
EXECUTE IMMEDIATE vRep_string;
I am getting "INVALID SQL STATEMENT" error
Why?
Could you, please help.

Thanks,
Eugene


Tom Kyte
September 09, 2004 - 7:36 am UTC

why would you do that dynamically?????????????

just

begin
.....

dbms_snapshot.refresh( 'UB_OBJECTS.BO_SORD_SUMMARY', 'c' );
.....

end;


??? (in any case, a dynamically invoked plsql block would have the begin/end wrapped around them -- execute immediate 'begin dbms_snapshot.....'


I cannot even figure out why you are using DUAL? and not just vRep_string :=

Nor why all of the concatention?


but anyway, just code it as I did above.

exception

Raaghid, September 11, 2004 - 3:42 am UTC

Begin
Execute immediate t_exp into t_remark ;
dbms_output.put_line(t_remark);
Exception
when no_data_found then
....
when others then
.....
end;

In the above, how to catch the exception if the sql (t_exp) is not a valid sql ie. having semantic or Syntax error. (ofcourse others is catching, but any unique exception available?)



Tom Kyte
September 11, 2004 - 10:39 am UTC

ops$tkyte@ORA9IR2> declare
  2      invalid_sql exception;
  3      pragma exception_init( invalid_sql, -900 );
  4
  5      l_str  long;
  6      l_data long;
  7  begin
  8      l_str := 'foobar';
  9      execute immediate l_str into l_data;
 10  exception
 11      when INVALID_SQL
 12      then
 13          dbms_output.put_line( '"' || l_str || '" is invalid sql');
 14          raise;
 15  end;
 16  /
"foobar" is invalid sql
declare
*
ERROR at line 1:
ORA-00900: invalid SQL statement
ORA-06512: at line 14
 

Name of argument passed in

A reader, September 13, 2004 - 3:51 pm UTC

I have a tiny private procedure in a larger package that validates its input according to some rules

procedure foo (p_arg in varchar2,p_value in out varchar2)
is
begin
-- validate stuff
if (error) then
raise_application_error(-20000,'Arg: '||p_arg||' Invalid value: '||p_value);
end if;
p_value := p_arg;
end;

Here, for p_arg, I really want the name of the argument passed in, not its value

Yes, I can do

foo('l_arg',l_arg,l_value);

But the code looks not so good.

Is there a way to pass in a 'string' and "dereference" it?

Sort of like pointer variables in C and dereferencing them with the *variable notation?

Thanks



Tom Kyte
September 13, 2004 - 8:27 pm UTC

that would not be at all like passing a pointer in C, IN OUT and OUT is exactly like passing a pointer in C.

There is no way to pass in a 'string' with a locally scoped variable name in it and "dereferencing" it, no.

I don't understand why you have to pass 'l_arg' and l_arg -- doesn't l-arg have the name in it?

A reader, September 13, 2004 - 8:53 pm UTC

"I don't understand why you have to pass 'l_arg' and l_arg -- doesn't l-arg have the name in it? "

l_arg is the value of the variable l_arg.

In my error message, I want to say that the parameter passed in ('l_arg' had a bad value l_arg (without the quotes)

I guess

foo('l_arg',l_arg,...) is the only way?

Thanks

Tom Kyte
September 13, 2004 - 9:20 pm UTC

what is l_value then?

A reader, September 13, 2004 - 10:17 pm UTC

l_value is the value I want to set it to. Its a component of the %rowtype variable for a record in the target table, its passed in as a OUT variable to foo

If l_arg passes validation, I will assign l_value := l_arg



Tom Kyte
September 14, 2004 - 7:29 am UTC

ok, well, there still isn't going to be a way to dereference a local variable (which wouldn't even be in scope in the called subroutine) -- the only thing you can do is pass the name and the value and the secondary value.

Dereference

A reader, September 14, 2004 - 9:44 am UTC

Oh well, its just that foo('l_arg',l_arg,l_value) looks a little odd.

My C pointer analogy was not correct, I meant the 'eval' type of feature in Unix shells where you can do this kind of stuff like

x=5
y=x
eval echo '$'$y

this prints 5

dynamic update

Xavi, January 18, 2005 - 5:46 am UTC

Hi

I need to convert a PRO*C code into PL/SQL.

I have two tables, t1 and t2

t1 has three columns, cod_area, nam_area, description

t2 has four columns cod_area, mod_date, nam_area, description

t2 is basically a table to support incremental modifications, mod_date is the modification date

so whenever there is a row in t2 and that row is in t1 what we do is update t1 with values in t2, however we only update the column where the values are different, let´s say only nam_area is modified which was YORKSHIRE after modification it becomes LONDON, so instead of

update t1
set (cod_area, nam_area, description) = (select cod_area, nam_area, description from t2 where cod_area = 1)
where cod_area = 1;

we do

update t1
set nam_area = 'LONDON'
where cod_area = 1

the PRO*C does this, it first queries t1 and t2

select * from t1 where cod_area = 1
select cod_area, nam_area, description from t2 where cod_area = 1

store the output in variables then uses strcmp to compare variables from both result sets, if different it builds a string for dynamic SQL, can we achieve this with PL/SQL?

I am arguing with the support guy, I am telling him that doing this sort of operation is easier with PL/SQL but he insists PRO*C is much faster.

Tom Kyte
January 18, 2005 - 10:03 am UTC

why not update all of the columns -- this should not be code at all -- this SCREAMS "make me a single update please!!!!"


I'd erase all code.

because of bitmap indexes

Xavi, January 18, 2005 - 10:21 am UTC

Hi

I am told not to update all row because there are bitmap indexes and bitmap indexes needs to be rebuilt very often when there are DML occuring in the indexed columns.

I suggested the same too, updating the row and forget it but the guy uses the bitmap index theory

Tom Kyte
January 18, 2005 - 11:35 am UTC

:)  -- couple of things.

first, they seem to be updating "slow by slow" (eg: row by row).  If your primary goal is to destroy a bitmap index, that is by far THE SINGLE BEST way to do it!!!

(that is, their approach is 100% counter to their argument)


if you want a bitmap to be well preserved -- you try to do as much as you humanly can in a single SQL statement.

Second, when you update a column to it's current value (eg: you do not change the value), we skip the index alltogether.  Optimization for tools that update every column in a table even if they are not changed (eg: forms always used to do that...)  

Consider:


ops$tkyte@ORA9IR2> create table t       as select * from big_table.big_table where rownum <= 100000;
 
Table created.
 
ops$tkyte@ORA9IR2> create table t2 as select * from t;
 
Table created.
 
ops$tkyte@ORA9IR2> alter table t2 add constraint t2_pk primary key(id);
 
Table altered.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> create table istats as select rpad('x',10,'x') when, index_stats.* from index_stats where 1=0;
 
Table created.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> begin
  2      for x in (select column_name from user_tab_columns where table_name = 'T' )
  3      loop
  4          execute immediate 'create bitmap index bm_' || x.column_name || ' on t(' || x.column_name || ')';
  5          execute immediate 'analyze index bm_' || x.column_name || ' validate structure';
  6          insert into istats select '1 before', index_stats.* from index_stats;
  7          dbms_stats.gather_index_stats( user, 'BM_' || x.column_name );
  8      end loop;
  9  end;
 10  /
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> @p
ops$tkyte@ORA9IR2> create or replace procedure prt ( p_str in varchar2 )
  2  is
  3     l_str   long := p_str;
  4  begin
  5     loop
  6        exit when l_str is null;
  7        dbms_output.put_line( substr( l_str, 1, 250 ) );
  8        l_str := substr( l_str, 251 );
  9     end loop;
 10  end;
 11  /
 
Procedure created.
 
ops$tkyte@ORA9IR2> declare
  2      l_stmt long;
  3  begin
  4      l_stmt := 'update ( select ';
  5      for x in (select column_name from user_tab_columns where table_name = 'T' )
  6      loop
  7          l_stmt := l_stmt || ' t1.' || x.column_name || ' t1_' || x.column_name || ',';
  8          l_stmt := l_stmt || ' t2.' || x.column_name || ' t2_' || x.column_name || ',';
  9      end loop;
 10      l_stmt := rtrim( l_stmt, ',' ) || ' from t t1, t2 where t1.id = t2.id ) set ';
 11      for x in (select column_name from user_tab_columns where table_name = 'T' and column_name <> 'ID')
 12      loop
 13          l_stmt := l_stmt || ' t1_' || x.column_name || ' = ' || ' t2_' || x.column_name || ',';
 14      end loop;
 15      l_stmt := rtrim( l_stmt, ',' );
 16      execute immediate l_stmt;
 17      dbms_output.put_line( sql%rowcount || ' rows modified' );
 18      prt( l_stmt );
 19      for x in (select column_name from user_tab_columns where table_name = 'T' )
 20      loop
 21          execute immediate 'analyze index bm_' || x.column_name || ' validate structure';
 22          insert into istats select '2 after', index_stats.* from index_stats;
 23      end loop;
 24  end;
 25  /
<b>100000 rows modified</b>
update ( select  t1.ID t1_ID, t2.ID t2_ID, t1.OWNER t1_OWNER, t2.OWNER t2_OWNER, t1.OBJECT_NAME t1_OBJECT_NAME, t2.OBJECT_NAME t2_OBJECT_NAME, t1.SUBOBJECT_NAME t1_SUBOBJECT_NAME, t2.SUBOBJECT_NAME t2_SUBOBJECT_NAME, t1.OBJECT_ID t1_OBJECT_ID, t2.OBJ
ECT_ID t2_OBJECT_ID, t1.DATA_OBJECT_ID t1_DATA_OBJECT_ID, t2.DATA_OBJECT_ID t2_DATA_OBJECT_ID, t1.OBJECT_TYPE t1_OBJECT_TYPE, t2.OBJECT_TYPE t2_OBJECT_TYPE, t1.CREATED t1_CREATED, t2.CREATED t2_CREATED, t1.LAST_DDL_TIME t1_LAST_DDL_TIME, t2.LAST_DDL_
TIME t2_LAST_DDL_TIME, t1.TIMESTAMP t1_TIMESTAMP, t2.TIMESTAMP t2_TIMESTAMP, t1.STATUS t1_STATUS, t2.STATUS t2_STATUS, t1.TEMPORARY t1_TEMPORARY, t2.TEMPORARY t2_TEMPORARY, t1.GENERATED t1_GENERATED, t2.GENERATED t2_GENERATED, t1.SECONDARY t1_SECONDA
RY, t2.SECONDARY t2_SECONDARY from t t1, t2 where t1.id = t2.id ) set  t1_OWNER =  t2_OWNER, t1_OBJECT_NAME =  t2_OBJECT_NAME, t1_SUBOBJECT_NAME =  t2_SUBOBJECT_NAME, t1_OBJECT_ID =  t2_OBJECT_ID, t1_DATA_OBJECT_ID =  t2_DATA_OBJECT_ID, t1_OBJECT_TYP
E =  t2_OBJECT_TYPE, t1_CREATED =  t2_CREATED, t1_LAST_DDL_TIME =  t2_LAST_DDL_TIME, t1_TIMESTAMP =  t2_TIMESTAMP, t1_STATUS =  t2_STATUS, t1_TEMPORARY =  t2_TEMPORARY, t1_GENERATED =  t2_GENERATED, t1_SECONDARY =  t2_SECONDARY
 
PL/SQL procedure successfully completed.
 
ops$tkyte@ORA9IR2>
ops$tkyte@ORA9IR2> column leaf format a15
ops$tkyte@ORA9IR2> column branch format a10
ops$tkyte@ORA9IR2> column hgt format 99
ops$tkyte@ORA9IR2> column blk format 999
ops$tkyte@ORA9IR2> column dlr format 99
ops$tkyte@ORA9IR2> break on name skip 1
ops$tkyte@ORA9IR2> select name, when, height hgt, blocks blk, lf_rows||'/'||lf_blks||'/'||lf_blk_len leaf, br_rows ||'/'||br_blks||'/'||br_blk_len branch, del_lf_rows dlr
  2    from istats
  3   order by name, when;
 
NAME              WHEN       HGT  BLK LEAF            BRANCH     DLR
----------------- ---------- --- ---- --------------- ---------- ---
BM_CREATED        1 before     2   32 753/25/7996     24/1/8028    0
                  2 after      2   32 753/25/7996     24/1/8028    0
 
BM_DATA_OBJECT_ID 1 before     2   16 1362/9/7996     8/1/8028     0
                  2 after      2   16 1362/9/7996     8/1/8028     0
 
BM_GENERATED      1 before     2    8 6/3/7996        2/1/8028     0
                  2 after      2    8 6/3/7996        2/1/8028     0
 
BM_ID             1 before     2  384 100000/348/7996 347/1/8028   0
                  2 after      2  384 100000/348/7996 347/1/8028   0
 
BM_LAST_DDL_TIME  1 before     2   32 828/26/7996     25/1/8028    0
                  2 after      2   32 828/26/7996     25/1/8028    0
 
BM_OBJECT_ID      1 before     2  256 27935/128/7996  127/1/8028   0
                  2 after      2  256 27935/128/7996  127/1/8028   0
 
BM_OBJECT_NAME    1 before     2  256 16099/133/7996  132/1/8028   0
                  2 after      2  256 16099/133/7996  132/1/8028   0
 
BM_OBJECT_TYPE    1 before     2   16 29/5/7996       4/1/8028     0
                  2 after      2   16 29/5/7996       4/1/8028     0
 
BM_OWNER          1 before     2    8 25/3/7996       2/1/8028     0
                  2 after      2    8 25/3/7996       2/1/8028     0
 
BM_SECONDARY      1 before     2    8 6/3/7996        2/1/8028     0
                  2 after      2    8 6/3/7996        2/1/8028     0
 
BM_STATUS         1 before     2    8 7/3/7996        2/1/8028     0
                  2 after      2    8 7/3/7996        2/1/8028     0
 
BM_SUBOBJECT_NAME 1 before     2    8 35/3/7996       2/1/8028     0
                  2 after      2    8 35/3/7996       2/1/8028     0
 
BM_TEMPORARY      1 before     2    8 7/3/7996        2/1/8028     0
                  2 after      2    8 7/3/7996        2/1/8028     0
 
BM_TIMESTAMP      1 before     2   32 791/26/7996     25/1/8028    0
                  2 after      2   32 791/26/7996     25/1/8028    0
 
 
28 rows selected.

<b>so, we updated every column for 100,000 rows -- and each column was bitmap indexed and.....

it didn't touch them.

And if it had -- it would have done THE LEAST AMOUNT OF DAMAGE TO THEM since bitmaps LOVE bulk operations</b>


So, if their goal is

a) speed
b) least amount of damage to the bitmap

they have done the single worst thing they could -- slow by slow processing!


 

indeed, the process goes row by row

Xavi, January 18, 2005 - 12:20 pm UTC

Hi

The process goes row by row, it´s compulsory because it´s an ETL Process.

My question is, are you saying that when we update the whole row if the value of the bitmap indexed columns are not changed the bitmap index size remains the same?

I will try a test now.

Cheers

Tom Kyte
January 18, 2005 - 12:59 pm UTC

why does ETL make it "compulsory"

ETL doesn't mean "hey, lets find the slowest most resource instensive way to do stuff and do it"

ETL means "extract, transform, load". transform doesn't have to be slow by slow.

Updates don't kill bitmap indexes,
People kill bitmap indexes by going slow by slow.

update row by row

Xavi, January 18, 2005 - 2:09 pm UTC

Hi

The process is done row by row because each row are treated seperately with some logic (if else and some calculations). Also the exceptions needs to be handled for each row, we can have different situations that´s why a bulk update wouldnt work.

I will carry the test tonight by updating row by row some bitmapped columns


Cheers

Tom Kyte
January 18, 2005 - 3:25 pm UTC

still the wrong way.

suggestion

Xavi, January 18, 2005 - 3:57 pm UTC

Hi

I have been looking the code, my thoughts were as yours, this can be done in bulk but after reviewing the requirements and code analysis I conclude that this has to be done row by row. This is one of those typical processes where if the row is there then update otherwise insert just that instead of updating the whole row only modified portions need to be modified.

I am not what´s the correct way to do this? We are using 8.1.7.4 so merge statement cant be used

Cheers

Tom Kyte
January 19, 2005 - 10:01 am UTC

you might consider putting the modifications into a global temporary table.... with an "insert" "update" "delete" flag (array inserting with forall if you can)....

and then at the update

delete where in
update ( a join )
insert as select

do three bulk operations (which in 10g will be a single merge, in 9i a merge and a delete....) and be done with it.

the bitmaps will be the better for it, and if you can forall i insert say 100/500 rows at a time -- even better.

A bit of misunderstanding

Gints, January 19, 2005 - 4:06 am UTC

So abstacting from that static SQL should be used whenever possible and dynamic if only truly needed I'm a bit confused.
So i took your approach and created two procedures:
create or replace procedure inst1 is
begin
for i in 1..10000 loop
execute immediate 'insert into t values (1, 1)';
end loop;
end;
/
create or replace procedure inst2 is
a number :=1;
b number := 1;
begin
for i in 1..10000 loop
execute immediate 'insert into t values (:a, :b)' using a, b;
end loop;
end;
/
Running them with runstats showed that first one runs a little faster, has little less latches (~10%). Can you shed some light on it?

Tom Kyte
January 19, 2005 - 10:42 am UTC

tell me how many times you would actually execute insert into t values (1,1) ?

would you really insert 10,000 rows with the same precise values?

I don't think so.

So, you would have 10,000 UNIQUE sql's really -- in the real world.

And you are not comparing static sql to dyanmic sql here at all are you? (no, rhetorical question).


so, compare static sql to dynamic sql if that is your goal - and VARY the inputs as they would be IN REAL TRUE LIFE.

ran the test

Xavi, January 19, 2005 - 7:18 am UTC

Hi

I ran the test, when the bitmap indexed columns are not updated then the index size remains the same.

This leads to me to do another test testing out why update only modified column is better, it turns out modifying only the modified columns is faster which is logical I think. I guess we must do it this way since out tables has around 150 columns!



create table mega_tabla nologging
as
select rownum id, a.* from dba_objects a where 1 = 0;

insert /*+ append */ into mega_tabla
select '' id, a.* from dba_objects a;

commit;

insert /*+ append */ into mega_tabla
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a;

commit;

insert /*+ append */ into mega_tabla
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a
union all
select a.* from mega_tabla a;

commit;

update mega_tabla set id = rownum ;

commit;

create table t
as select * from mega_tabla
where rownum <= 10000;

create table t2 as select * from t;

alter table t add constraint t_pk primary key(id);

alter table t2 add constraint t2_pk primary key(id);

begin
for i in (select * from t where id between 1 and 10000)
loop
update t2 set
OBJECT_NAME = i.OBJECT_NAME
where t2.id = i.id+1;
end loop;
end;
/

exec runStats_pkg.rs_start

begin
for i in (select * from t2 where id between 1 and 10000)
loop
update t set t.object_name = i.object_name where t.id = i.id;
end loop;
end;
/

exec runStats_pkg.rs_middle

begin
for i in (select * from t2 where id between 1 and 10000)
loop
update t set OWNER = i.OWNER,
OBJECT_NAME = i.OBJECT_NAME,
SUBOBJECT_NAME = i.SUBOBJECT_NAME,
OBJECT_ID = i.OBJECT_ID,
DATA_OBJECT_ID = i.DATA_OBJECT_ID,
OBJECT_TYPE = i.OBJECT_TYPE,
CREATED = i.CREATED,
LAST_DDL_TIME = i.LAST_DDL_TIME,
TIMESTAMP = i.TIMESTAMP,
STATUS = STATUS,
TEMPORARY = i.TEMPORARY,
GENERATED = i.GENERATED,
SECONDARY = i.SECONDARY
where t.id = i.id;
end loop;
end;
/

exec runStats_pkg.rs_stop

exec runStats_pkg.rs_stop
Run1 ran in 1645 hsecs
Run2 ran in 1854 hsecs
run 1 ran in 88,73% of the time

Name Run1 Run2 Diff
LATCH.dml lock allocation 1 0 -1
STAT...SQL*Net roundtrips to/f 4 5 1
STAT...execute count 10,006 10,007 1
STAT...parse count (total) 7 8 1
STAT...redo log space requests 0 1 1
STAT...write clones created in 0 1 1
STAT...session cursor cache co 1 2 1
STAT...redo entries 10,005 10,006 1
STAT...redo buffer allocation 0 1 1
STAT...opened cursors cumulati 7 8 1
LATCH.archive control 0 2 2
STAT...user calls 6 8 2
STAT...session cursor cache hi 5 3 -2
LATCH.loader state object free 0 2 2
STAT...parse count (hard) 0 2 2
STAT...enqueue requests 5 3 -2
LATCH.archive process latch 0 2 2
LATCH.session idle bit 12 16 4
STAT...redo log space wait tim 0 4 4
STAT...consistent changes 21 17 -4
LATCH.active checkpoint queue 8 14 6
LATCH.list of block allocation 6 0 -6
LATCH.Token Manager 0 7 7
STAT...change write time 92 100 8
STAT...calls to get snapshot s 10,011 10,020 9
STAT...consistent gets 40,013 40,023 10
LATCH.row cache objects 0 12 12
STAT...messages sent 14 26 12
LATCH.enqueue hash chains 5 20 15
LATCH.redo allocation 10,039 10,067 28
LATCH.enqueues 9 40 31
LATCH.redo writing 52 88 36
LATCH.messages 80 135 55
STAT...recursive cpu usage 380 441 61
STAT...CPU used by this sessio 409 482 73
STAT...CPU used when call star 409 482 73
LATCH.cache buffers lru chain 375 450 75
LATCH.library cache 20,053 20,134 81
LATCH.shared pool 1 102 101
STAT...bytes sent via SQL*Net 540 653 113
LATCH.checkpoint queue latch 651 766 115
STAT...db block changes 20,156 20,302 146
STAT...free buffer requested 133 283 150
STAT...db block gets 10,282 10,577 295
STAT...session logical reads 50,295 50,600 305
LATCH.cache buffers chains 111,220 111,866 646
STAT...bytes received via SQL* 930 1,648 718
STAT...session uga memory max 5,912 4,236 -1,676
STAT...session uga memory 15,344 4,236 -11,108
STAT...redo size 2,671,132 5,084,448 2,413,316

Run1 latches total versus runs -- difference and pct
Run1 Run2 Diff Pct
142,513 143,724 1,211 99.16%



for 10000 rows it generates 50% more redo, since we must process 10 million per day I think we should modify only the modified rows what do you think?






Tom Kyte
January 19, 2005 - 10:44 am UTC

i think code should be easy, small, maintainable and done IN BULK.

I'm with Tom

Stew, January 19, 2005 - 11:32 am UTC

Tom concluded: "I think code should be easy, small, maintainable and done IN BULK."

To Xavi:

Fast and efficient are terrific goals.

MAYBE if Tom were the lead programmer in this project, could see the source, know the entire business environment and requirements, he MIGHT (or might not) agree that modifying only the columns that changed made sense. But I suspect he'd STILL work out some way to do this using bulk operations.

You "modify as needed" code might work now, but pity the next coder to maintain it. If they break some little feature you've worked in because they couldn't understand what was happening, you're hosed.

Besides, this site, while for the real world, is so all of us can learn from Tom's expertise. In the real world, in all but the most unique cases, his mantra works best:

"I think code should be easy, small, maintainable and done IN BULK."

Problem with Dynamic SQL

Sanjeev Sapre, January 31, 2005 - 6:43 am UTC

Tom
In order to create a generic procedure for our specific need I was trying to to write a code like this.

declare

TYPE cur_type IS ref CURSOR ;
TYPE rec_type IS record ( col user_tab_columns.column_name%TYPE );
mycur cur_type ;
myrec rec_type ;

rec1 sgst1%rowtype ;
rec2 sgst2%rowtype ;

s varchar2(100);
sql_stmt VARCHAR2(200);

begin
select * into rec1 from sgst1 WHERE ROWNUM < 2 ;
select * into rec2 from sgst2 WHERE ROWNUM < 2 ;
--- comapre logic
open mycur FOR 'select column_name from all_tab_columns where table_name = sgst1 ' ;
LOOP
FETCH mycur into myrec ;
exit WHEN mycur%notfound ;
sql_stmt := ' select rec1.'||myrec.col ||' FROM dual ' ;
EXECUTE IMMEDIATE sql_stmt into s ;
END LOOP ;
END ;
/

when i run this

I get following

ERROR at line 1:
ORA-00904: "SGST1": invalid identifier
ORA-06512: at line 14

Please explain why this is not working ...

thanks
Sanjeev



Tom Kyte
January 31, 2005 - 8:28 am UTC

<quote>
generic procedure for our specific need
</quote>

an oxymoron -- i laughed out loud as I read it.

I wish you would have said in english what your goal was instead of posting some somewhat obtuse code. I'm not sure at all what your goal is -- especially with the rownum stuff, looping over all of the column names using dynamic sql that should ONLY BE STATIC SQL. building an invalid sql statement based on that. Trying to repeatedly put the value into some local variable that is never used -- and that the last value is the only relevant value since all others get over written.

Sorry -- no clues -- not at all sure what you are trying to do


I can say "generic code" can also many times be considered "evil code". Many times specific code is much better (faster, easier to understand, less buggy, more maintainable)

Let me clarify

Sanjeev Sapre, February 02, 2005 - 8:20 am UTC

Ok

Let me clarify
Generic.. I wanted to write a code for comparing records.

Rownum here was here to create two records in this sample code.

My attempt is to fetch value for a perticular column from the record. and I am trying to do this with the help of dynamic SQL. In order to get the column name i an using all_tab_columns.

Sanjeev


Tom Kyte
February 02, 2005 - 8:27 am UTC

don't. bad idea, horrible idea. besides, you are not going to dynamically access a "record" like that.


if you want to compare two rows - I can show you the easy fast sql for that. to compare two records -- well, you the programmer already know how to do that, no generic code for that. (you the programmer *better* know the fields don't you think)

Some more clarification

Sanjeev, February 02, 2005 - 8:25 am UTC

Yes and about the repeatedly putting value into same variable..I have not added code that will actual compare two records because of the error..

Sanjeev


more explaination needed..

A reader, February 07, 2005 - 5:34 am UTC

don't. bad idea, horrible idea. besides, you are not going to dynamically
access a "record" like that.


=> Yes ,, in fact that is the question.. why cant we do that ?

if you want to compare two rows - I can show you the easy fast sql for that.

=> are u talking of minus ?

to compare two records -- well, you the programmer already know how to do that, no generic code for that. (you the programmer *better* know the fields don't you
think)

=> u mean that is not possible.. The need was to send a mail if thee is a difference. Mail only those columns those have differences.

Sanjeev


Tom Kyte
February 07, 2005 - 6:16 am UTC

who is "u", "u" keeps getting requests -- but I've never met them (not really sure of the gender either, is "u" male or female") anyway.


If you have two rows in a table and would like to compare them column by column and print out only those that differ - I can do that (in fact, if you have Effective Oracle by Design -- i do it in there to show differences all of the time)


If you as a programmer have two records and you want to print out the differences YOU the programmer have everything you already need to print that out:


if ( rec1.field1 <> rec2.field1 ) then .....


(taking care with NULLS if need be). there would be no reason to access it dynamically, you wrote the code, you know the records attributes.


execute immediate

omer, March 02, 2005 - 1:17 am UTC

Hi!
I am trying to put value in varaiable according to the cursor value.eg

SQL> select loc,code
2 from site
3 /

L CODE
- ---------
A 300
B 100

declare
adept NUMBER;
bdept NUMBER;
begin
for i_Rec in (select loc,code from site) loop
irec.loc||'dept' := irec.code;
end loop;
end;

but error come. is it possible by using execute immediate or by any way 

thanx
 

Tom Kyte
March 02, 2005 - 7:12 am UTC

sorry, but I cannot decipher what that code is even attempting to do?

You have a single variable "I_REC"

This variable has two attributes -- I_REC.LOC and I_REC.CODE

l_rec.loc || 'dept' is what is known as an "rvalue" -- a right hand side value. It can be on the right hand side of an assigment.

It has no meaning as an "lvalue", something to be assigned to.

dynamic sql

omer, March 03, 2005 - 3:15 am UTC

hi!

>>sorry, but I cannot decipher...

SQL> desc site
 Name                            Null?    Type
 ------------------------------- -------- ----
 LOC                                      CHAR(1)
 CODE                                     NUMBER


SQL> select loc,code
  2  from   site
  3  /

L      CODE
- ---------
A       300
B       100

declare
adept NUMBER;
bdept NUMBER;
begin
for iRec in (select loc,code from site) loop
if irec.loc = 'A' THEN
adept:= irec.code;
elsif irec.loc = 'B' THEN
bdept:=irec.code;
end if;
end loop;
end;

for loc "A" i have variable adept and for loc "B" i have bdept variable.
in above code i have use if then else. i want to avoid this
because i have 50 to 60 statements, thats why i want to do like this

irec.loc||'dept' := irec.code;
mean if irec.loc will "A" it's valaue will be return in adept variable and in case of "B" bdept will receive the value.

can this be done using dynamic sql... 

thanx  

Tom Kyte
March 03, 2005 - 7:33 am UTC

why would you have 50 variables named that?


I would

ops$tkyte@ORA9IR2> declare
  2          type array is table of number index by varchar2(30);
  3
  4          l_data  array;
  5  begin
  6          for x in ( select dname loc, deptno code from scott.dept )
  7          loop
  8                  l_data(x.loc) := x.code;
  9          end loop;
 10
 11
 12          dbms_output.put_line( 'Accounting ' || l_data( 'ACCOUNTING' ) );
 13          dbms_output.put_line( 'Research   ' || l_data( 'RESEARCH' ) );
 14  end;
 15  /
Accounting 10
Research   20
 
PL/SQL procedure successfully completed.



if you need this, 50 variables -- you would be "if elsifing" all over the place 

Dynamic call of the stored procedure with parameters

Jatin, March 16, 2005 - 11:22 pm UTC

Dear Tom,

In you answer earlier in this topic, you suggested to user following structure:
l_block :=
'declare
l_start_date date := :x1;
l_end_date date := :x2;
l_seq_id number := :x3;
l_desc long := :x4;
begin
' || ...... passing l_ parameters || ';
end;';
for dynamic calls of the stored procedure. I know that it is very important to use bind variables for dynamic SQL, but would it make much difference if I put parameter values straight into dynamic pl/sql procedure call?
E.G
......
lsSql := 'begin my_procedure1(';
for i in ( select value,
datatype
from Proc_parameter pp,
proc_to_run p
where p.proc_name = 'MY_PROCEDURE1'
and p.status = 'NEW'
and p.id = pp.run_id
order by pp.param_orderno )
loop
if (i.datatype = TYPE_BOOLEAN) then
if (upper(i.value) = 'Y' ) then
lsParam := 'TRUE';
else
lsParam := 'FALSE';
end if;
elsif (i.datatype = TYPE_NUMBER) then
lsParam := i.value;
elsif (i.datatype = TYPE_STRING) then
lsParam := ''''||i.value||'''';
end if;

lsSql := lsSql || lsParam || ',';
end loop;
execute immediate lsSQL;
......

Thank you so much for your help.


Tom Kyte
March 17, 2005 - 8:30 am UTC

use binds
use binds
use binds
use binds
use binds
use binds.......................



Dynamic call of the stored procedure with parameters

Jatin, March 17, 2005 - 4:00 pm UTC

Dear Tom,

Thank you for your reply. I understand that binds are better, but I am confused on how to do it in situation. I have table PROC which contains list of procedures to be called and table ATTR which contains parameters definitions used in calling of the stored procedures from PROC. These tables linked many to many via PROC_ATTR table.
I also have table PROC_TO_RUN which contains procedures to be executed and PROC_PARAMETER which contains links to ATTR, PROC_TO_RUN and actual value of the parameter.
Unfortunately I don't see how I can use suggested by you approach because I have over 100 unique parameters in table ATTR, but each procedure has normally less than 10 of them.
Please suggest the best way of using bind variables instead of "hard coded" values in the dynamic call to those procedures.
Thank you very much for your help.

Tom Kyte
March 17, 2005 - 4:17 pm UTC

you can either

a) use the application context technique demonstrated here:
</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:1288401763279 <code>

b) use dbms_sql, build a string like:


begin procedure(:parm1, :parm2, :parm2, :parm4, .... ); end;

use dbms_sql to parse that, then use dbms_sql to bind by name (using the same query you are using now to glue the values in hard coded) and then execute it.

some tip for dynamic

sadiq, March 18, 2005 - 4:01 am UTC

create or replace function wipckfun(m_item varchar2,m_yyyymm number) return number is
--m_item varchar2(10):='330120120';
--m_yyyymm number(6):=200501;
m_opqty number(12,3);
m_recqty1 number(12,3);
m_recqty2 number(12,3);
m_recqty3 number(12,3);
m_pcqty number(12,3);
m_rejqty number(12,3);
m_srsqty number(12,3);
m_srsqty2 number(12,3);
m_pmrqty number(12,3);
m_pmiqty number(12,3);
m_adjqty number(12,3);
totqty number(12,3);
pqty number(12,3);
nqty number(12,3);
begin
select sum(open_qty) into m_opqty from wipledcb
where yyyymm = m_yyyymm
and itemcode=m_item;
dbms_output.put_line('opqty '||m_opqty);
------------------------
select sum(quantity) into m_recqty1
from dirmat.dayledger
where transtag = '2'
and cardcode in ('MRCIV')
and to_number(to_char(confirm_date,'yyyymm')) >= m_yyyymm
and itemcode=m_item;
dbms_output.put_line('mrciv '||m_recqty1);
------------------------
select sum(batchqty) into m_recqty2
from workorder.workorder_header
where to_number(to_char(authorisedon,'yyyymm')) >= m_yyyymm
and nvl(status,0) = 'Y'
and rmitem=m_item;
dbms_output.put_line('workorder '||m_recqty2);
------------------------
select sum(issued_qty) into m_recqty3
from mtl_request
where nvl(issued_qty,0) !=0
and to_char(updated_on,'yyyymm') >= m_yyyymm
and rmitem=m_item;
dbms_output.put_line('mtl_req '||m_recqty3);
------------------------
select sum(PCQTY) into m_pcqty
from prodstrpcblow
where yyyymm >=m_yyyymm
and minor=m_item;
dbms_output.put_line('pcqty '||m_pcqty);
------------------------
select sum(rejqty) into m_rejqty
from prodstrrejblow
where rejtype!='MATERIAL'
and yyyymm >=m_yyyymm
and minor=m_item;
dbms_output.put_line('rejqty '||m_rejqty);
------------------------
select sum(srsqty) into m_srsqty
from prodstrsrsblow
where yyyymm >=m_yyyymm
and minor=m_item;
dbms_output.put_line('srsqty '||m_srsqty);
------------------------
select sum(rejqty) into m_srsqty2
from prodstrrejblow
where rejtype='MATERIAL'
and yyyymm >=m_yyyymm
and minor=m_item;
dbms_output.put_line('srsqty2 '||m_srsqty2);
------------------------
select sum(pmrqty) into m_pmrqty
from prodstrpmrblow
where yyyymm >=m_yyyymm
and minor=m_item;
dbms_output.put_line('pmrqty '||m_pmrqty);
------------------------
select sum(pmiqty) into m_pmiqty
from prodstrpmiblow
where yyyymm >=m_yyyymm
and minor=m_item;
dbms_output.put_line('pmiqty '||m_pmiqty);
------------------------
select sum(qty) into m_adjqty
from wipadj
where to_number(to_char(docdate,'yyyymm')) >=m_yyyymm
and status != '9'
and item=m_item;
dbms_output.put_line('adjqty '||m_adjqty);
------------------------
pqty:=nvl(m_opqty,0)+nvl(m_recqty1,0)+nvl(m_recqty2,0)+
nvl(m_recqty3,0)+nvl(m_pmiqty,0)+nvl(m_adjqty,0);
nqty:=nvl(m_pcqty,0)+nvl(m_rejqty,0)+nvl(m_srsqty,0)+nvl(m_srsqty2,0)+nvl(m_pmrqty,0);
dbms_output.put_line('pqty '||pqty||' nqty '||nqty);
totqty:=(nvl(m_opqty,0)+nvl(m_recqty1,0)+nvl(m_recqty2,0)+
nvl(m_recqty3,0)+nvl(m_pmiqty,0)+nvl(m_adjqty,0))-
(nvl(m_pcqty,0)+nvl(m_rejqty,0)+nvl(m_srsqty,0)+nvl(m_srsqty2,0)+nvl(m_pmrqty,0));
dbms_output.put_line('total '||totqty);
return(totqty);
end;

Above procedure is working fine but
After seeing the above reviews I feel above procedure is very bad way of coding
Hello Tom can you give some tips to convert the above procedure using dynamic method or some good method


Tom Kyte
March 18, 2005 - 7:06 am UTC

I'd be asking myself "why, why do I need to count rows in so many tables"


but, it is already written 'well'. Static SQL is always to be prefered over dynamic sql in plsql -- always.

you use dynamic sql if and only if you cannot do it statically.

about the only change you might make would be:


select sum(q) into totqty
from ( select sum(qty) Q from ..... where....
UNION ALL
select sum(qty) Q from .... where ...
UNION ALL
..... )




table name from variable in select

Sujit, March 23, 2005 - 2:12 pm UTC

CREATE OR REPLACE PROCEDURE sp_test IS

r NUMBER;
t varchar2(30) := 'd_customer';
x varchar2(200);

BEGIN

execute immediate 'select count(*) from :x' INTO r USING t;
dbms_output.put_line('test:'||r);

END sp_test;
/

ORA-00903: invalid table name
ORA-06512: at "TGTMIT.SP_TEST", line 9
ORA-06512: at line 2

Tom Kyte
March 23, 2005 - 6:22 pm UTC

think about it. why do you use bind variables?

to avoid the hard parse which includes query plan generation

what happens if you change the table name?

you must hard parse in order to get the right plan......


you cannot bind IDENTIFIERS, it changes the plan, the security, EVERYTHING.


.... from ' || t into r;



dynamic SQL in 10g and strange sql in the trace file

A reader, March 29, 2005 - 5:25 am UTC

Hi

I have this code

declare
l_sql long;
l_table varchar2(30) := 'EMP';
l_ename varchar2(10);
begin
l_sql := 'select ename from '||l_table||' where ename = ''KING''';
execute immediate l_sql into l_ename;
dbms_output.put_line('['||l_ename||']');
end;
/

I sql traced the session using 10046 event then I see in the trace file this SQL

SELECT /* OPT_DYN_SAMP */ /*+ ALL_ROWS IGNORE_WHERE_CLAUSE
NO_PARALLEL(SAMPLESUB) NO_PARALLEL_INDEX(SAMPLESUB) */ NVL(SUM(C1),0),
NVL(SUM(C2),0)
FROM
(SELECT /*+ IGNORE_WHERE_CLAUSE NO_PARALLEL("EMP") FULL("EMP")
NO_PARALLEL_INDEX("EMP") */ 1 AS C1, CASE WHEN "EMP"."ENAME"='KING' THEN 1
ELSE 0 END AS C2 FROM "EMP" "EMP") SAMPLESUB

Why is this query used internally by Oracle? It turns out that when using dynamic SQL I am actually querying twice the table!

Tom Kyte
March 29, 2005 - 8:26 am UTC

that is dynamic sampling (if you have access to Effective Oracle by Design, I wrote about it in chapter 6)

The problem here is -- you are doing a hard parse (NO BIND VARIABLE!!! ugh, gotta use them).

You are using the CBO (10g I assume)

The CBO is brain dead without stats.

So the first time it is hard parsing, against a table without statistics, it is doing a quick sample of the table to discover "what it is like".

Without that information, the query plan developed would not be what you wanted in general.




dynamic from clause

A reader, March 29, 2005 - 8:58 am UTC

Hi

declare
l_sql long;
l_table varchar2(30) := 'EMP';
l_ename varchar2(10);
begin
l_sql := 'select ename from '||l_table||' where ename = ''KING''';
execute immediate l_sql into l_ename;
dbms_output.put_line('['||l_ename||']');
end;
/

I cant use bind variable because the table_name is dynamic. Or is there any way which I dont know :-?

And yes, the table didnt have any stats....

cheers

Tom Kyte
March 29, 2005 - 10:30 am UTC

l_sql := 'select ename from ' || l_table || ' where name = :x';

execute immediate l_sql into l_ename using 'KING';


A dynamic sql problem

Arun Gupta, April 06, 2005 - 10:27 am UTC

Tom,
Please see the quote from PL/SQL User Guide and Reference page 11-15 (Native Dynamic SQL)
=================================================
Using Duplicate Placeholders
Placeholders in a dynamic SQL statement are associated with bind arguments in the USING clause by position, not by name. So, if the same placeholder appears two or more times in the SQL statement, each appearance must correspond to a bind
argument in the USING clause. For example, given the dynamic string
sql_stmt := ’INSERT INTO payroll VALUES (:x, :x, :y, :x)’;
you might code the corresponding USING clause as follows:
EXECUTE IMMEDIATE sql_stmt USING a, a, b, a;
However, only the unique placeholders in a dynamic PL/SQL block are associated
with bind arguments in the USING clause by position. So, if the same placeholder
appears two or more times in a PL/SQL block, all appearances correspond to one
bind argument in the USING clause. In the example below, the first unique
placeholder (x) is associated with the first bind argument (a). Likewise, the second
unique placeholder (y) is associated with the second bind argument (b).
DECLARE
a NUMBER := 4;
b NUMBER := 7;
BEGIN
plsql_block := ’BEGIN calc_stats(:x, :x, :y, :x); END;’
EXECUTE IMMEDIATE plsql_block USING a, b;
...
END;
=========================================
I am doing as

create table t1(
id_pmt number(10),
id_en number(10),
id_pay number(10));

Then write an anonymous PL/SQL block:

declare
v_Query varchar2(100);
inv number(10);
pmt number(10);
begin
v_Query := 'Select * from t1 where id_pmt=:x and id_en=:y and id_pay=:y';

Execute immediate v_Query using INV,PMT;

end;

I get ora-01008 not all variables bound error.

If I change v_Query to:
v_Query := 'Select * from t1 where id_pmt=:x and id_en=:y';

then the block works fine.

The database is 9.2.0.5. How can I make it work like mentioned in the Oracle documentation?

Thanks

Tom Kyte
April 06, 2005 - 1:29 pm UTC

could well be it is a documentation bug. Please contact support for this.

what engine does PRO*C use?

A reader, April 15, 2005 - 4:43 am UTC

Hi

In 8i we had seperate engine for SQL and PL/SQL, in 9i and 10g they are merged into one.

In PRO*C however to use analtyic functions in 8i, 9i ad 10g we still need to use dynamic SQL. My question is, what engines does PRO*C use?

Cheers



Tom Kyte
April 15, 2005 - 9:11 am UTC

separate "parser", there is only one sql engine.

plsql had a sql parser and that parser did not always understand the newest sql constructs.

pro*c unfortunately is still in this category, the pre-processor uses its own sql parser and it does not understand analytics as yet.



seems that Oracle doesnt like C

A reader, April 15, 2005 - 10:27 am UTC

Hi

It seems that PRO*C is not being improved by the vendor, in this case Oracle. How so? PRO*C has stayed in the same technology track since 1997 or so! No improvements at all and the future seems dark as well.
PL/SQL is cool but for massive data processes such as billing application are much faster in C than pure PL/SQL.

Cheers

Tom Kyte
April 15, 2005 - 10:45 am UTC

Variable Use

Mita, May 19, 2005 - 6:58 pm UTC

create table x (a number);

begin
for a in 1..5
loop
insert into x values (a);
end loop;
end;
/

create procedure test as
b number := 10;
begin
insert into x(a)
select b
from (select b
from x
where a = 2);
end;
/

create package test1 as
procedure ins(z number) ;
end;
/

create package body test1 as
procedure ins(z number) is
b number := 20;
begin
insert into x(a)
select b
from (select b
from x
where a = z);
end;
end;
/

begin
test;
test1.ins(1);
test1.ins(2);
end;
/





DDMMGR@DTSTGDOD> select * from x ;

A
----------
1
2
3
4
5
10
1
2

8 rows selected.

in case of package why did it insert 1 & 2 where as in procedure it inserted 10 ??



Tom Kyte
May 20, 2005 - 7:13 am UTC

that code does not run on my system and when I made it run, I got 10, 20, 20

changes

Mita, May 20, 2005 - 7:38 am UTC

what changes you needed to do in order to make this work ?? On my database, this runs without any issues. and as you see the output, it is all weird.



Tom Kyte
May 20, 2005 - 10:24 am UTC

well, see the other place you posted this, but they are apples and oranges (z in one, not the other)

9i rejects the select b from ( select b from ....
and wants select * from ( select b from ....

in 8i, if you make the queries the same -- they behave the same. it is an issue, and as mentioned on the OTHER place you put this, please contact support.

TESTER says

TESTER, May 20, 2005 - 9:41 am UTC

SQL>
SQL> begin
  2      test;
  3      test1.ins(1);
  4      test1.ins(2);
  5  end;
  6  /
begin
*
Error in line 1:
ORA-00904: "B": invalid identifier
ORA-06512: at "TESTER.TEST", line 4
ORA-06512: at line 2

 

Tom Kyte
May 20, 2005 - 6:24 pm UTC

and if you do what i said above...

generic code

Ray DeBruyn, May 25, 2005 - 1:58 pm UTC

I have created a package with three functions. They return Strings as CREATE OR REPLACE for a package, an insert trigger and an update trigger. I pass the table name and schema name of the table I wish to audit and return the string to create the package. I create package variables, one for each column in the table and also create an insert statement. To do this I have a cursor that selects from all_tab_columns.

This works if I run it from the schema that owns the table, but the cursor returns no records if I run it from another schema. I can run the query in the other schema in SQLPlus and get the correct records. The grantee of all_tab_columns is public which seems to appear as a direct grant.

My workaround is to run the functions in the table owners schema. Can you give any insight as to why a cursor selecting column_name from all_tab_columns would only return records where the table is owned by that schema when run in a procedure or anonymous block and yet return more records when run in SQLPlus?

Interested in Boston

u no wut i mean? [just kidding on the IM speak. I agree with you]

Tom Kyte
May 25, 2005 - 3:35 pm UTC

can you give me a simple example -- eg:

create user a;
create user b;
grant....
connect a
build something
grant to b
run something, see results
connect b
run something else



The all_views can be very tricky, especially in stored procedures -- they have "security" builtin to them.

The security looks at the current environment to a degree. So, I'd need an example to "explain why"

And then suggest that this probably should be an INVOKERS rights (authid current_user) procedure. so it runs with the privs of the INVOKER

connecting to other user dynamically

sns, June 24, 2005 - 3:35 pm UTC

I have a situation to change the password of other user using a stored procedure.

Login as user A using sqlplus. Execute a stored procedure that changes the password of user B. The old password and the new password along with the username are the parameters of the stored proc.

This is what I did:

create or replace procedure change_others_password(p_user_id in varchar2,
p_old_password in varchar2,
p_new_password in varchar2)
as
v_sql varchar2(1000);
v_user varchar2(30);
begin

select username into v_user
from dba_users
where username=sys_context('userenv','session_user');

dbms_output.put_line('before user '||v_user);

v_sql := 'connect '||p_user_id||'/'||p_old_password||'@estrp';
dbms_output.put_line(v_sql);
execute immediate v_sql;

select username into v_user
from dba_users
where username=sys_context('userenv','session_user');

dbms_output.put_line('after user '||v_user);

v_sql := 'alter user '||p_user_id||' identified by '||p_new_password||' replace '||p_old_password||';';
--dbms_output.put_line(v_sql);
execute immediate v_sql;
end;

I get the error right at connect to the user B.

Do you a better idea how to achieve this using a stored Proc?

thanks,

Tom Kyte
June 24, 2005 - 6:49 pm UTC

you cannot connect in a stored procedure, you are "already connected", if you tried to disconnect to connect -- you would have to kill the running procedure, because it cannot run without being connected.

what are you trying to accomplish by connecting?

and be careful with dynamic sql.

...., p_new_password => 'foobar quota unlimited on system' );

for example, you need to have lots of validation on inputs you glue into SQL statements. quota, default roles, proxy connections - lots of stuff could be slipped in there.

connecting dynamically to a different user,

sns, June 24, 2005 - 3:35 pm UTC

I have a situation to change the password of other user using a stored procedure.

Login as user A using sqlplus. Execute a stored procedure that changes the password of user B. The old password and the new password along with the username are the parameters of the stored proc.

This is what I did:

create or replace procedure change_others_password(p_user_id in varchar2,
p_old_password in varchar2,
p_new_password in varchar2)
as
v_sql varchar2(1000);
v_user varchar2(30);
begin

select username into v_user
from dba_users
where username=sys_context('userenv','session_user');

dbms_output.put_line('before user '||v_user);

v_sql := 'connect '||p_user_id||'/'||p_old_password||'@estrp';
dbms_output.put_line(v_sql);
execute immediate v_sql;

select username into v_user
from dba_users
where username=sys_context('userenv','session_user');

dbms_output.put_line('after user '||v_user);

v_sql := 'alter user '||p_user_id||' identified by '||p_new_password||' replace '||p_old_password||';';
--dbms_output.put_line(v_sql);
execute immediate v_sql;
end;

I get the error right at connect to the user B.

Do you a better idea how to achieve this using a stored Proc?

thanks,

connect to another user,

sns, June 25, 2005 - 7:29 am UTC

I am trying to accomplish to change the password of the other user.  If you want to change the password of a user, you log in as that user, change the password and exit out.

SQL> show user;
abc

SQL> connect xyz/oracle@estrp;
connected:

SQL> alter user xyz identified by new_oracle replace oracle;

SQL> disconnect;

Here I logged in as abc (initially), then connected as xyz, changed the password of xyz and disconnected from xyz.

I want to accomplish something like this using a stored proc.  The stored proc is owned by user abc.  The parameters I supply to my stored proc is user(xyz), old password(oracle) and new password (new_oracle).  It should change the password of the user xyz and exit out.

Is it possible to accomplish?
If so how?

thanks,
 

Tom Kyte
June 25, 2005 - 9:46 am UTC

No, you do not.


You need to create a procedure as a user that has been graned the ALTER USER privilege.


ALTER USER allows you to change anyones password. Create a schema, grant create session, alter user to that schema. Create the stored procedure (with the appropriate checks on the input strings and whatever validation you want to have that says "yes, user A is allowed to change user B's password"). Grant execute on that procedure to those that should have it.


sns, June 25, 2005 - 11:45 am UTC

If the user A has grants to change the password of user B, then how does the stored proc looks like?

Let me try to understand your explanation clearly:
If user A does not have grants to change the password of user B, then there is no way using a stored proc to login as user B (from user A), change the password of user B (here I am changing the password of user B being user B NOT user A) and then exit out.

Thanks,


Tom Kyte
June 25, 2005 - 1:13 pm UTC

If you want A to be able to change B's password (although, that seems "strange" doesn't it?), it could look like this:


ops$tkyte@ORA9IR2> create user a identified by a;
User created.
 
ops$tkyte@ORA9IR2> create user b identified by b;
User created.
 
ops$tkyte@ORA9IR2> grant create session to a;
Grant succeeded.
 
ops$tkyte@ORA9IR2> grant create session, create procedure to b;
Grant succeeded.
 
ops$tkyte@ORA9IR2> @connect b/b
ops$tkyte@ORA9IR2> set termout off
b@ORA9IR2> set termout on
b@ORA9IR2> create procedure change_b( p_new_password in varchar2 )
  2  as
  3          l_whitespace varchar2(20) := ' ' || chr(10) || chr(13) || chr(9);
  4  begin
  5          if ( p_new_password = replace( translate(p_new_password,l_whitespace,' '), ' ', '' ) )
  6          then
  7                  execute immediate 'alter user b identified by ' || p_new_password;
  8          else
  9                  raise_application_error( -20001, 'no way' );
 10          end if;
 11  end;
 12  /
Procedure created.
 
b@ORA9IR2> grant execute on change_b to a;
Grant succeeded.
 
b@ORA9IR2> @connect a/a
b@ORA9IR2> set termout off
a@ORA9IR2> set termout on
a@ORA9IR2>
a@ORA9IR2> exec b.change_b( 'newpassword' );
 
PL/SQL procedure successfully completed.
 
a@ORA9IR2> connect b/b
ERROR:
ORA-01017: invalid username/password; logon denied
 
 
Warning: You are no longer connected to ORACLE.
a@ORA9IR2> connect b/newpassword
Connected.
 

thanks a lot,

sns, June 26, 2005 - 7:39 am UTC

I appreciate your time for answering my question.



sorry one more question,

sns, June 27, 2005 - 10:51 am UTC

When does one require to use the keyword "replace" when issuing "alter user" command to change the password?

Is this mandatory on some situation?

thanks,

How to compare 2 tables and columns dynamically??

A reader, July 14, 2005 - 2:16 pm UTC

HOw can I do the above? I am trying to find it in your
website, but I can't.

Tom Kyte
July 14, 2005 - 3:01 pm UTC

</code> http://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:2151582681236#15364732017705 <code>

You can use the techniques listed there, write a stored procedure that queries the dictionary to build the sql and returns a dynamically opened ref cursor easily enough.

Can i use column name as bind variable.

Amit Singh, July 29, 2005 - 3:11 am UTC

Hi Tom,
I have a situation where i have to keep the value of the column name in some variable and then prepare a sql statemnt and do Execute Immediate to get the value of the column.I have done in this way ,but i am not getting the correct result.What is the way around

column_name :=DM_NHS_NUMBER; --this is coming through some logic.
sql_stmt1 :='SELECT :ID1 FROM PMI_STAGING WHERE DM_SEQ =:dm_seq1';
EXECUTE IMMEDIATE sql_stmt1 INTO column_value USING COLUMN_NAME,DM_SEQ1;

Tom Kyte
July 29, 2005 - 8:39 am UTC

You cannot bind identifiers.

(I don't like your situation, ugh, generic like this is good for one thing, making things really confusing long term. I'll bet they end up calling this little, inefficient, bad procedure for each colum one at a slow time....)

you have to construct a unique sql statement for each column. The PLANS can be different for EACH one.


execute immediate 'select ' || p_column_name|| '
from pmi_staging
where dm_seq = :x' into p_column_value using p_dm_seq1;


the more I look at that, the worse I feel about it. I assume pmi_staging has more than one datatype in it too. too bad they'll all be in strings. ugh.


seriously suggest you look at your plsql naming convention and use p_ for parameters, l_ for local variables.

Why no DDL in stored procedure

Syed, August 04, 2005 - 7:18 am UTC

Hi Tom

reading this thread, I notice on several occaisions you say
DDL in stored procedures is not a good idea.

Can you give some reasons why ?

thanks

Syed

Tom Kyte
August 04, 2005 - 9:38 am UTC

why do you think it would be good?

it is expensive to perform
it would make you do everything in dynamic sql
it is not necessary


so, why do you think that it would be good?

Static SQL to Dynamic SQL

A reader, August 11, 2005 - 11:32 am UTC

Tom,
Need your expertise on my situation.

We currently wrote some packages[which has procedures with same functionality, in all the packages].

eg: package 1:
Procedure DATAOUT (inp IN integer,
DataOut OUT sys_refcursor)
is

Begin

Open DataOut as Select [List ofcolumns] from [table_name] where id=inp;

End;

Function InsertData(list of columns as IN parameters)
return integer
is
v_id in integer:=0;
Begin
Insert into table_name[columns]..values [list of parameters] returning id into v_id;
return v_id
exception
[exception handling]
return 0;
end;


eg: package 2:
Procedure DATAOUT (inp IN integer,
DataOut OUT sys_refcursor)
is

Begin

Open DataOut as Select [List ofcolumns] from [table_name] where id=inp;

End;

Function InsertData(list of columns as IN parameters)
return integer
is
v_id in integer:=0;
Begin
Insert into table_name[columns]..values [list of parameters] returning id into v_id;
return v_id
exception
[exception handling]
return 0;
end;

Each package is written for each Table (object from front end);

We are thinking to Generalise all the functions instead of repeating same functionality for every object (in every package).
But if I want to do that, then i have to
1) Make the static SQL's as Dynmaic SQL's (as input parameters for InsertData function will be an Array,with table name passed as another parameter) and construct column values from that array.
2) And to find columns dynamically for each table, we need to access user_tab_columns and get column_names from it.

My Question is , is it really worth doing this way , or just keep everything like we currently have and get on with it? Can you explain what's the best approach [with valid reasons..much better]

I remember reading from your responses , you prefer keeping it as Static SQL's then dynamic unless required.

Thanks for any comments you have on this,


Tom Kyte
August 12, 2005 - 7:58 am UTC

DON'T GENERALIZE.


don't do it, you only lose things, you gain NOTHING

a) dependency mechanism -- gone
b) ability for code to be self healing when you modify the base table -- gone
c) performance -- out the window
d) scalability -- lost, as you'll be parsing like mad

there is nothing good about it, lots bad.

Actually the entire approach of a "table API" to me is about as wrong as you can get. You should have a package full of entries like

hire_emp
fire_emp
transfer_emp
promote_emp
give_raise_to_emp
appraise_emp

and so on. That is API's that are transactions with the sql needed to do them. table apis, don't like them.

But there is zero percent chance I'll help you make a generic package using dynamic sql. the goal is not the least amount of code, it is not golf, our goal is to develop performant, scalable, *maintainable*, debuggable systems. This does not help us achieve stated goal.

Did not get a and b

A reader, August 12, 2005 - 10:41 am UTC

Tom,
Thanks for your reply. I told my group that we are not Generalizing.
But I did not get points
a) dependency mechanism -- gone
b) ability for code to be self healing when you modify
the base table -- gone

Can you eloborate on this.

Thanks a lot.

Tom Kyte
August 13, 2005 - 8:57 am UTC

alter table t add x number;
alter table t modify y varchar2(80);

static sql -> self healing code
dynamic sql -> errors pop up randomly at runtime when users hit them.

NO dependency mechanism, no invalidations, no automatic code fixes.

If you have access to my book effective oracle by design, I elaborate on this and more in great detail.

ALTER PACKAGE statement using DYNAMIC SQL

Puja, October 26, 2005 - 8:57 am UTC

Hi Tom,

I have a user named app_owner who owns certain packages that are also used by another user named 'puja'.

I want 'puja' to check the status of packages that she is using(belonging to app_owner) and compile them if they are found 'invalid'. We don't wish to give any privileges directly to 'puja' and hence I created the following procedure as 'app_owner'

SQL> show user
USER is "APP_OWNER"
SQL> ed
Wrote file afiedt.buf

  1  CREATE OR REPLACE PROCEDURE CHK
  2  AS
  3  STR VARCHAR2(100);
  4  CURSOR C IS select object_name, status from USER_objects where OBJECT_TYPE LIKE 'PACKAGE%';
  5  BEGIN
  6  FOR I IN C LOOP
  7  IF I.STATUS='INVALID' THEN
  8  STR:='ALTER PACKAGE '||I.OBJECT_NAME||' COMPILE';
  9  EXECUTE IMMEDIATE STR;
 10  END IF;
 11  END LOOP;
 12* END;
SQL> /

Procedure created.

SQL> grant execute on chk to puja;

Grant succeeded.

SQL> conn puja/puja@rdb
Connected.
SQL> exec app_owner.chk;
ERROR:
ORA-24344: success with compilation error
ORA-06512: at "APP_OWNER.CHK", line 9
ORA-06512: at line 1



Warning: PL/SQL compilation errors.

SQL> show err
No errors.


I thought the problem is arising because I had not appended schema name to the package name. so to test it, I went back to app_owner, but on executing the procedure, i got the same error:

SQL> show user
USER is "APP_OWNER"
SQL> exec chk;
ERROR:
ORA-24344: success with compilation error
ORA-06512: at "APP_OWNER.CHK", line 9
ORA-06512: at line 1



Warning: PL/SQL compilation errors.

SQL> show err
No errors.
SQL>

Can you please tell me where I went wrong?

Thanks and regards

Puja 

Tom Kyte
October 26, 2005 - 12:28 pm UTC

show error without any inputs only works if you just issued the create in sqlplus (then sqlplus knows the name)

query the *_errors views to see the error. (* = user, all, dba)

Another problem with the same procedure

Puja, October 26, 2005 - 11:33 pm UTC

Hi,

Now I am able to execute the procedure 'chk' without any errors. But it is not compiling the packages (which it is supposed to do)

I have a package 'test' that is containing reference to the table 'abc' in the same schema.

Initially 'test' is valid.
SQL> select object_name, status from USER_objects where OBJECT_TYPE LIKE 'PACKAGE%';

OBJECT_NAME          STATUS
-------------------- -------
BASE_RATE_UPDATE_PKG VALID
BASE_RATE_UPDATE_PKG VALID
DATAPORTING          VALID
DATAPORTING          INVALID
............


TEST                 VALID
TEST                 VALID


.............

Now I alter the structure of 'abc'. It invalidates the package 'test'

SQL> alter table abc add (z number);

Table altered.

SQL> select object_name, status from USER_objects where OBJECT_TYPE LIKE 'PACKAGE%';

OBJECT_NAME          STATUS
-------------------- -------
BASE_RATE_UPDATE_PKG VALID
BASE_RATE_UPDATE_PKG VALID
DATAPORTING          VALID
DATAPORTING          INVALID
.....
TEST                 VALID
TEST                 INVALID
......


I execute the procedure chk (which has to check what all packages are invalid and then compile them)

SQL> exec chk;

PL/SQL procedure successfully completed.

SQL> select object_name, status from USER_objects where OBJECT_TYPE LIKE 'PACKAGE%';

OBJECT_NAME          STATUS
-------------------- -------
BASE_RATE_UPDATE_PKG VALID
BASE_RATE_UPDATE_PKG VALID
DATAPORTING          VALID
DATAPORTING          INVALID
.......
TEST                 VALID
TEST                 INVALID
.................

But on explicitly giving the compile statement on SQL prompt, the package status becomes VALID:

SQL> alter package test compile;

Package altered.

SQL> select object_name, status from USER_objects where OBJECT_TYPE LIKE 'PACKAGE%';

OBJECT_NAME          STATUS
-------------------- -------
BASE_RATE_UPDATE_PKG VALID
BASE_RATE_UPDATE_PKG VALID
DATAPORTING          VALID
DATAPORTING          INVALID
.........

TEST                 VALID
TEST                 VALID

Why does the ALTER PACKAGE --- COMPILE; doesn't work inside the procedure?

Regards,

Puja 

Tom Kyte
October 27, 2005 - 6:41 am UTC

why don't you throw some dbms_output.put_lines into your chk code to see what it says it is actually doing (eg: debug code)


funny thing about your procedure however could be.....

let's say A depends on B. A and B are procedures.

Let's say A and B are BOTH invalid.

Let's say your chk routine compiles both A and B in the order of A and then B.


Well, when you compile A, that'll implicitly compile B - After A is compiled both A and B are valid...

But then you compile B, the act of compiling B will invalidate A!!!!

So, you end up with

A invalid
B valid




Thanks..problem solved

Puja, October 27, 2005 - 12:05 am UTC

HI Tom,

The problem is solved now.

Thanks

Puja

Question about Execute immediate

Lilian, November 06, 2005 - 4:13 pm UTC

Hello Tom,

I'm facing a problem trying to use "Execute immediate" in a procedure. 
What I'm trying to do is to list a first table (dept) and with the colum result of this (loc) I try to do a select * of tables wich names are in loc.
I try to do this using an array in the Execute immediate statement but I get the following error:

Procedure
=======================================================
create or replace procedure proc_startup  is
    str     varchar2(200);
    str1    varchar2(200);
    type    my_curs_type is REF CURSOR;  
    curs    my_curs_type;
    ret     varchar2(200);
    type    string_tab is table of varchar2(20);
    ret_tab string_tab;
begin
    str := 'select loc from dept';
    DBMS_OUTPUT.PUT_LINE(str);
    OPEN curs FOR str;
    FETCH curs BULK COLLECT INTO ret_tab;
    dbms_output.put_line('Table names: ');
    for i in 1..curs%rowcount loop
       dbms_output.put_line(str); 
       execute immediate 'select * from ' || i.ret_tab;
    end loop;
  CLOSE curs;
end;
=======================================================
When I run this I get a compilation error:

SQL> @c:/proc_startup_examp.sql
 33  /

Warning: Procedure created with compilation errors.

SQL> show errors
Errors for PROCEDURE PROC_STARTUP:

LINE/COL ERROR                                                                  
-------- --------------------------------------------------     
27/8     PL/SQL: Statement ignored                                              
27/48    PLS-00487: Invalid reference to variable 'I' 

==========================================================
When I change the Execute immediate statement for this:

   execute immediate 'select * from ' || ret_tab(i);

the procedure compiles without errors but I get the followoing error when trying to execute the procedure:

SQL> @c:/proc_startup_examp.sql
 33  /

Procedure created.

SQL> show errors
No errors.
SQL> exec proc_startup
select loc from dept                                                            
BEGIN proc_startup; END;

*
ERROR at line 1:
ORA-00942: table or view does not exist 
ORA-06512: at "SYSTEM.PROC_STARTUP", line 17 
ORA-06512: at line 1 
-----------------------------------------------------
PLEASE tell me what am I doing wrong and how can I correct my procedure.

The result that I'm looking for is:

select loc from dep => T1, T2, T3

select * from loc => select * from T1;
                     select * from T2;
                     select * from T3;

and overall that these last select's get actually executed!

Thank you so much for your advice!

Regards,

 

 

Tom Kyte
November 06, 2005 - 5:50 pm UTC

you cannot do the implicit "for I in" with an explicit cursor...

open curs for string;
loop
fetch curs into variables...
exit when curs%notfound;
....

you have to fetch it into your own set of variables...

just make sure you really need dynamic sql - only use it when you cannot do what you need with static sql...

if you think about it - there is no way at compile time for plsql to know what columns are in your query!

Question about Execute immediate

Lilian, November 07, 2005 - 5:38 am UTC

Hi again,

Thank you for your answer. Would you be so kind to give me an example that i can use? Thanks again!

Regards.

Tom Kyte
November 07, 2005 - 9:02 am UTC

I did???

You open the cursor, you fetch from it into variables???

here is all of the details in the plsql doc:

</code> http://docs.oracle.com/docs/cd/B19306_01/appdev.102/b14261/sqloperations.htm#i7106 <code>

Dynamic PL/SQL

Suvendu, November 17, 2005 - 1:51 pm UTC

Hi Tom,

The problem is seems simple but I'm not getting the answer here and also couldn't get any answer from this forum too. The question which I described thru the PL/SQL block where I wants to pass row%type attribute dynamically into dbms_output.put_line funtion to display. Is there such way similar to Dynamic PL/SQL it can be acomplished.

Correct me if I'm wrong.

Thanking you for your kind help and time.

Regards,
Suvendu


---------------------------

drop table t;

create table t as select * from all_objects where rownum<=5;

declare
t_row t%rowtype;
v_tot_col pls_integer:=0;
v_col_name varchar2(30);
v_dynamic_col varchar2(40);
begin
select * into t_row from t where rownum=1;

select count(1) into v_tot_col from user_tab_columns where table_name='T';

for i in 1.. v_tot_col
loop
select column_name into v_col_name from user_tab_columns
where table_name='T' and column_id=i;

v_dynamic_col:=t_row||'.'||v_col_name;

dbms_output.put_line(v_col_name||' = '||v_dynamic_col);
end loop;

end;



Tom Kyte
November 18, 2005 - 9:56 am UTC

you cannot dynamically access a record like that no.

if you want to procedurally process the results of a query like that, you would use DBMS_SQL which gives you a 100% procedural API to access the columns of a result set.

EXECUTE IMMEDIATE and privilegies

Dmytro, December 21, 2005 - 5:27 am UTC

Sorry, don't know how to ask question the other way...

Have the problem like this: freely dropping and altering schema objects through dynamic sql, but can't create any new - insufficient privilegies error. What's interesting, this error I have only in named blocks (procedures, functions etc.), anonymous ones are running freely. Adding AUTHID CURRENT_USER solves the problem, but leaves me a little confused. Some privilegies are granted through roles, maybe this causes the problem? Oracle 9
Any links to the documentation on this cause will be greatly appreciated.

My best regards...

Tom Kyte
December 21, 2005 - 7:32 am UTC

Thanks a lot!

Dmytro, December 22, 2005 - 3:20 am UTC

How can I miss such a thing?.. Next time will be more attentive, when reading documentation.

Tom Kyte
December 22, 2005 - 10:45 am UTC

No worry - it is a subtle point and one that is missed many times. I wrote an entire chapter about it (definer/invoker rights routines) in Expert one on one Oracle in fact...

Procedure throwing error

Vikas Khanna, December 30, 2005 - 10:01 am UTC

Hi Tom,

I am writing the procedure for Rolling Windows partitioning and trying to add a new partition and simultaneously drop the oldest partion.

I am dynamically preparing a statement and then using execute immediate trying to execute it, but it is failing

The statement so constructed is like this

add_partition_string := '''' || 'Alter table ' || tables.table_name || ' add partition ' ||  p_partition_new_name
     || ' VALUES LESS THAN (to_date(' || '''' || to_char(p_partition_new_date,'DD/MM/YYYY hh24:mi:ss') || '''' || ',' || '''' ||'DD/MM/YYYY hh24:mi:ss' || '''' || '))' || '''' ;

'Alter table INVOICES add partition P_20010106 VALUES LESS THAN
(to_date('07/01/2001 00:00:00','DD/MM/YYYY hh24:mi:ss'))'

but when I say:

execute immediate add_partition; it gives me an error:
ORA-00900: invalid SQL statement

However from SQL> prompt the statement is getting executed.

Please let us know why it is throwing the error

Thanks

 

Tom Kyte
December 30, 2005 - 11:11 am UTC

well, no sql starts with quote

why do you have the leading and trailing quotes?

Informative answers - Thanks

Joshua, January 30, 2006 - 7:42 pm UTC

I solved my very critical problem with the questions and responses in this forum.

Thanks a lot Tom

Joshua

Sanjeev Vibuthi, February 01, 2006 - 2:55 am UTC

Hi Tom,

Customer Table
Customer Code


Automobile
Customer Code
Product Code
Date of Purchase
Cost
Cultural
Customer Code
Product Code
Date of Purchase
Cost
Ornaments
Customer Code
Product Code
Date of Purchase
Cost
Cloths
Customer Code
Product Code
Date of Purchase
Cost

Like this i have 10 product tables, in all these 3 columns are common
i want to know Customer wise how much amount he has purchased on a given date........
I have written a Proc where i have put 10 queries to get sum of cost from all tables separately and calculated total
Is there any other method to get this result.....
it is not that a customer should buy all the products, he may buy product1 or Product 1 to 5 ...

Thanx in Adv.

Sanjeev Vibuthi


Tom Kyte
February 01, 2006 - 3:53 am UTC

select customer_code, sum(cost)
from (select customer_code, sum(cost) cost
from cloths
group by customer_code
union all
select customer_code, sum(cost) cost
from ornaments
group by customer_code
union all
......)
group by customer_code
;


and if you need it for one specfic customer code, just add the where clauses and remove the group by.

Thanx

Sanjeev Vibuthi, February 01, 2006 - 5:25 am UTC

Thanks Tom


Sanjeev Vibuthi, February 09, 2006 - 2:12 am UTC

Dear Tom,

scott@testdb10gR2-09-FEB-06>Create Table FIndex (Dcd Number(3), Ucd Number(3),
2 Fno Varchar(9), Mh Number(3), Mi Number(3),
3 stage char(3))
4 /

Table created.

scott@testdb10gR2-09-FEB-06>Create Table Stmt1 (Slno Number, Mh Number(3), Mi Number(3))
2 /

Table created.

scott@testdb10gR2-09-FEB-06>Create Table MM_Master (Mh Number(3), Mi Number(3))
2 /

Table created.

scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (1,10);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (1,11);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (1,12);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (1,13);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (2,21);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (2,22);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (3,31);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (4,41);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (4,42);
scott@testdb10gR2-09-FEB-06>Insert into MM_Master values (4,43);

scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'1/2005',1,10,'UI');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'2/2005',1,11,'PT');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'3/2005',1,13,'UI');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'4/2005',1,12,'CHD');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'5/2005',2,21,'UI');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'6/2005',2,22,'PT');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'7/2005',1,10,'UI');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'8/2005',4,41,'PT');
scott@testdb10gR2-09-FEB-06>Insert into findex values (22,46,'9/2005',4,42,'UI');

scott@testdb10gR2-09-FEB-06>Insert into stmt1 values (2,1,11);
scott@testdb10gR2-09-FEB-06>Insert into stmt1 values (3,1,0);
scott@testdb10gR2-09-FEB-06>Insert into stmt1 values (4,2,21);
scott@testdb10gR2-09-FEB-06>Insert into stmt1 values (5,2,0);
scott@testdb10gR2-09-FEB-06>Insert into stmt1 values (6,3,0);
scott@testdb10gR2-09-FEB-06>Insert into stmt1 values (7,4,41);
scott@testdb10gR2-09-FEB-06>Insert into stmt1 values (8,4,42);

scott@testdb10gR2-09-FEB-06>select * from findex;

DCD UCD FNO MH MI STA
---------- ---------- --------- ---------- ---------- ---
22 46 1/2005 1 10 UI
22 46 2/2005 1 11 PT
22 46 3/2005 1 13 UI
22 46 4/2005 1 12 CHD
22 46 5/2005 2 21 UI
22 46 6/2005 2 22 PT
22 46 7/2005 1 10 UI
22 46 8/2005 4 41 PT
22 46 9/2005 4 42 UI

9 rows selected.


scott@testdb10gR2-09-FEB-06>select * from mm_master;

MH MI
---------- ----------
1 10
1 11
1 12
1 13
2 21
2 22
3 31
4 41
4 42
4 43

10 rows selected.

scott@testdb10gR2-09-FEB-06>select * from stmt1;

SLNO MH MI
---------- ---------- ----------
1 1 10
2 1 11
3 1 0
4 2 21
5 2 0
6 3 0
7 4 41
8 4 42

8 rows selected.


scott@testdb10gR2-09-FEB-06>select mf.mh,stage,count(*) from
2 findex mf,
3 stmt1 st
4 where
5 st.mh=mf.mh and
6 (
7 st.mi<>0 and -- if mi value is <>0, select mi value from stmt1 table
8 mf.mi in
9 (select s2.mi from stmt1 s2
10 where s2.mh=mf.mh and st.slno=s2.slno)
11 ) OR
12 (
13 st.mi=0 and -- if mi value is 0, select mi value from mm_master table
14 mf.mi in
15 (select s3.mi from mm_master s3 where
16 s3.mh=mf.mh)
17 )
18 group by mf.mh,stage
19 /

MH STA COUNT(*)
---------- --- ----------
2 PT 3
1 PT 4
1 UI 11
2 UI 4
4 PT 4
4 UI 4
1 CHD 3

7 rows selected.

I got the result but MH, Stages are correct but Count is more than what it is there...

Can i give a sub query in where like this?

Thanx in Adv

Sanjeev Vibuthi





Tom Kyte
February 10, 2006 - 10:54 am UTC

don't take this wrong, but this is about the wrongest way to ask a question.


here is my data (that part is OK, good)
here is my query (this is where it goes really wrong)
I got the wrong answre (this is where it totally stops working all together)


All I have is a query that doesn't work, a result set you say is wrong but

o no explaination of the question you are trying to solve
o no idea what answer you are expecting



I am suspicious of AND's and OR's without lots of ()'s to make sure the coder knows what part comes first.


So, which one of the following did you mean to code?

scott@ORA10GR2> select * from dual where 1=0 and 1=0 or 1=1;

D
-
X

scott@ORA10GR2> select * from dual where 1=0 and (1=0 or 1=1);

no rows selected



Sanjeev Vibuthi, February 10, 2006 - 10:27 am UTC

Hi Tom,

I am waiting for ur reply.. becuase this query can solve my critical problems in some of major reports....

Whether this "type" of queries are available in any of your books.. ( I Have Expert.. and Effective... books) or I have to write a Proc and any other link which have similar queries in this site...

Thankx in Adv.

Sanjeev Vibuthi

Tom Kyte
February 10, 2006 - 1:03 pm UTC

You know, sometimes - just sometimes - I sleep.

Also, I travel.

I have my job too. (consider this my 'hobby')


but see above....

Sanjeev Vibuthi, February 11, 2006 - 1:16 am UTC

Sorry Tom,

Plz Dont mind to remind you again about my question... my necessity makes me to do so....

I thought that it is self explanatory ....

Now I'll explain what i required is...

I have a main table "findex", this table contains a FNO (File No) (DCD - Dist Code, UCD - Unit Code, FNO - Composite PK), MH (Main Head), MI (Minor Head), Current Stage of File..

My Actual Requirement is the following :
Stages
SLNO MH MI Description UI PT CHD
---------- ---------- ----------------------------------------------------------------------------------------------------------
1 1 10 Cycle Theft 1 1 0
2 1 11 3 Wheeler Theft 0 0 3
3 1 0 Automobile Theft 0 1 1
4 2 21 House Robbery 1 1 1
5 2 0 Robbery 2 2 0
6 3 0 Accident 2 2 1
7 4 41 Child Missing 3 1 2
8 4 42 Man Missing 2 1 3
9 4 0 Missing 2 1 3

In the final result "MI" column no need to display

MH - 1 means "Automobile Theft" under this MH, 10 - "Cycle Theft", 11 - "3 Wheeler Theft"
slno 1 contains Cycle Theft Stages (group by MH, Mi)
slno 2 contains 3 Wheeler Theft (group by MH, Mi)
slno 3 contains Total Automobile theft (group by MH)

In "Stmt1" table I am inserting slno, Mh, Mi, Description
If query i am checking it is is MI is 0, I am taking all MI for MH from MM_Master table (so i can get total under MH), if it is >0, I am taking only MI value from "Stmt1" table

select mf.mh,stage,Sum(Decode(stage,'UI',1,0)) "UI",
Sum(Decode(stage,'PT',1,0)) "PT",
Sum(Decode(stage,'CHD',1,0)) "CHD" from
findex mf,
stmt1 st
where
st.mh=mf.mh and
(
st.mi<>0 and
mf.mi in
(select s2.mi from stmt1 s2
where s2.mh=mf.mh and st.slno=s2.slno)
) OR
(
st.mi=0 and
mf.mi in
(select s3.mi from mm_master s3 where
s3.mh=mf.mh)
)
group by mf.mh,stage
/

With this query i will get MH, stage wise count, same will be updated in Stmt1 table and displayed


Sanjeev Vibuthi



Tom Kyte
February 11, 2006 - 2:37 pm UTC

is your OR correct there -- are you or are you not missing a set of paranthesis?


Your current query, as demonstrated with:

scott@ORA10GR2> select * from dual where 1=0 and 1=0 or 1=1;

D
-
X


returns the set of rows from

a) st joined to mf AND (subquery1)
B) (subquery2)

this just doesn't seem right. Please explicitly put () every where you can.

Did you mean:

(A and B) or C

or did you mean:

A and (B or C)


right now, you have (A and B) or C

Sanjeev Vibuthi, February 13, 2006 - 1:00 am UTC

Hi Tom,

Actually I want (A AND B) OR (A AND C)

SCOTT@ testdb 13-FEB-06>Select * from dual where (1=1 and 2=2) OR (1=1 and 3=3);

D
-
X

Initally it was like this ...
Declare
--- If both mh and mi are present
CURSOR c1 (v_mh NUMBER, v_mi NUMBER) IS
Select Sum(Decode(mh,v_mh, decode(mi,v_mi,
Decode(stage,'UI',1,0)))) "UI",
Sum(Decode(mh,v_mh, decode(mi,v_mi,
Decode(stage,'CHD',1,0)))) "CHD",
Sum(Decode(mh,v_mh, decode(mi,v_mi,
Decode(stage,'PT',1,0)))) "PT"
from findex
where mh=v_mh and
mi=v_mi;

--if only mh is present
CURSOR c2 (v_mh NUMBER) IS
Select Sum(Decode(mh,v_mh, Decode(stage,'UI',1,0))) "UI",
Sum(Decode(mh,v_mh, Decode(stage,'CHD',1,0))) "CHD",
Sum(Decode(mh,v_mh, Decode(stage,'PT',1,0))) "PT"
from findex
where mh=v_mh;

CURSOR c3 IS
SELECT * from stmt1;

rec1 c1%ROWTYPE;
rec2 c2%ROWTYPE;
rec3 c3%ROWTYPE;

Begin

Open c3;
Loop
Fetch c3 into rec3;
Exit when c3%notfound;

If rec3.mh<>0 AND rec3.mi<>0 then --If both are present
Open c1 (rec3.mh,rec3.mi);
Fetch c1 into rec1;
elsif rec3.mh<>0 AND rec3.mi=0 then -- if mh is present
Open c2 (rec3.mh);
Fetch c2 into rec2;
end if;
If c1%isopen then
dbms_output.put_line(' Both '||rec3.mh ||' - '|| rec3.mi ||' ='|| rec1.UI||'-'||rec1.PT||'-'||rec1.CHD);
else
dbms_output.put_line(' Only MH '||rec3.mh ||' ='|| rec2.UI||'-'||rec2.PT||'-'||rec2.CHD);
end if;
if c1%isopen then
close c1;
else
close c2;
end if;
end loop;
close c3;
end;
/

Now i want to make it in a single cursor (single query)...

I have changed the query as follows, but result count is not matching again...

select mf.mh, Sum(Decode(stage,'UI',1,0)) "UI",
Sum(Decode(stage,'PT',1,0)) "PT",
Sum(Decode(stage,'CHD',1,0)) "CHD" from
findex mf,
stmt1 st
where
( st.mh=mf.mh
and
st.mi<>0 and
mf.mi in
(select s2.mi from stmt1 s2
where s2.mh=mf.mh and st.slno=s2.slno)
)
OR
( st.mh=mf.mh and
st.mi=0 and
mf.mi in
(select s3.mi from mm_master s3 where
s3.mh=mf.mh)
)
group by mf.mh
/

Thanx in adv.

Sanjeev Vibuthi

Tom Kyte
February 13, 2006 - 8:16 am UTC

... Actually I want (A AND B) OR (A AND C) ...



so, you really really want

A and (b or c)


so, back to square one - as I've said over and over, you had:

(A and B) or C


this is boolean logic, should be rather straight forward.


If you want

st joined to mh

AND THEN a filter, it would resemble:

where sf JOINED to mf
and ( (condition1)
OR
(condition2)
)




Sanjeev Vibuthi, February 13, 2006 - 9:37 am UTC

hmmmm...

Thanx tom ... gr8 help .... it almost solved my problem...

In sql server i can write update statement like this :

Update emp set
ename=e1.ename,
sal=e1.sal
from emp, emp1 e1
where emp.empno=e1.empno

How can I Write the same statement in Oracle... It looks more convinient

Sanjeev Vibuthi

Tom Kyte
February 13, 2006 - 9:54 am UTC


You can use ansi sql:

update (select emp.ename, emp.sal, emp1.ename new_ename, emp1.sal new_sal
from emp, emp1
where emp.empno = emp1.empno )
set ename = new_name,
sal = new_sal;



Sanjeev Vibuthi, February 14, 2006 - 12:32 am UTC

Excellent...

Thanx Tom... Really ur gr8...



dynamic sql

A Reader, September 28, 2006 - 7:26 am UTC

Hi Tom

I have a database procedure that I need to test. Its definition is as under:

PROCEDURE test( p1 VARCHAR2,
p2 VARCHAR2,
p3 VARCHAR2,
p4 VARCHAR2,
p5 VARCHAR2,
p6 VARCHAR2,
p7 VARCHAR2,
p8 VARCHAR2 )
I need to test all the possible combinations of parameter values. For e.g. one test will be to just pass parameter p1. Another test will be to pass parameters p1 and p2 and so on. Is there any way to construct data source dynamically?

Thanks

Tom Kyte
September 28, 2006 - 7:39 am UTC

well, that isn't the definition of the procedure then, this procedure has no defaults :) hence you have to pass all 8 in all cases.


But yes, you can write any code to automate any sort of procedural stuff you need to do. Just "write a program".

Use INTO with Dynamic SQL

Jonty, October 12, 2006 - 9:41 am UTC

Hi,

I have to create a dynamic sql in my pl/sql code, and I want to get results into my variable.

How can I do this ?

Thanks

Jonty

Tom Kyte
October 12, 2006 - 11:41 am UTC

reading the documentation typically helps :)

there is

open ref_cursor for '.....';
fetch ref_cursor into <variables>


and there is:

execute immediate 'select .... from ....' into <variables>


depending on whether you need many or one rows.

</code> http://docs.oracle.com/docs/cd/B19306_01/appdev.102/b14261/dynamic.htm#i14500 <code>

Dynamic Implicit Cursor?

Greg, October 19, 2006 - 1:48 pm UTC

Hi Tom,

not sure if this is the best place to ask this .. but it seemed somewhat related ... I did some searching, and couldn't find any other places ..

This works using Explicit Cursor ...
================
set serverout on

DECLARE
lv_var1 NUMBER;

TYPE my_cur_type IS REF CURSOR;
my_cur my_cur_type;

c_loop NUMBER;
BEGIN

IF TRUE THEN -- ie some condition ...
OPEN my_cur FOR 'SELECT 1 abc FROM dual';
ELSE
OPEN my_cur FOR 'SELECT 2 abc FROM dual';
END IF;

LOOP
FETCH my_cur INTO c_loop;
EXIT WHEN my_cur%NOTFOUND;
lv_var1 := c_loop;
END LOOP;

CLOSE my_cur;

dbms_output.put_line ( lv_var1 );
END;
/
========================

Basically, it opens 1 of 2 cursors based on a decision (fixed for this example ...) fetches from it, and exits.

What I am curious about, however, is this:

Is there a way to accomplish the same (open 1 of 2 or more cursors based on a decision) using implicit cursors:

For example (this doesn't work ... but something like it would be nice?)
===============
set serverout on

DECLARE
lv_var1 NUMBER;

my_cur VARCHAR2(1000);

BEGIN

IF TRUE THEN -- ie some condition ...
my_cur := 'SELECT 1 abc FROM dual';
ELSE
my_cur := 'SELECT 2 abc FROM dual';
END IF;

FOR c_loop IN my_cur
LOOP
lv_var1 := c_loop;
END LOOP;

dbms_output.put_line ( lv_var1 );
END;
/
==============
Or even if there's a way to define the cursors in the declare block via:

cursor cur_1 is 'select 1 abc from dual';
cursor cur_2 is 'select 2 abc from dual';

.. then do either:
FOR c_loop IN cur_1
or
FOR c_loop IN cur_2

.. ???
Is this even possible using the FOR .. IN ... LOOP structure?
If so .. how do I pull this off?
(Yes, 10g ...)


Tom Kyte
October 19, 2006 - 2:19 pm UTC

I agree "it would be nice"

however, it won't work... doesn't work, not supported.

EXECUTE IMMEDIATE within FUNCTION

Suvendu, December 01, 2006 - 8:05 am UTC

Hi Tom,
Could you please, look into below few statements? 

Is there different behaviour of function having dynamic sql inside it, executing with EXEC command vs. calling with SELECT statement?

Kindly, currect me if I'm wrong.

Thanking you.

Regards,
Suvendu

-----------------------


M:\>sqlplus "scott/tiger"

SQL*Plus: Release 10.2.0.1.0 - Production on Fri Dec 1 18:05:26 2006

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


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> create or replace function fnc_drop_idx(p_index_name varchar2) return numb
  2  as
  3  l_index_name varchar2(30);
  4  begin
  5     select index_name into l_index_name
  6     from user_indexes
  7     where index_name=p_index_name;
  8
  9     if l_index_name is not null then
 10             execute immediate 'drop index '||p_index_name;
 11     end if;
 12     return 0;
 13  exception
 14     when others then
 15             null;
 16     return 1;
 17  end;
 18  /

Function created.

SQL> create index idx_job on emp(job);

Index created.

SQL> var x number
SQL> exec :x:=fnc_drop_idx('IDX_JOB');

PL/SQL procedure successfully completed.

SQL> print x

         X
----------
         0

SQL> create index idx_job on emp(job);

Index created.

SQL> select fnc_drop_idx('IDX_JOB') from dual;

FNC_DROP_IDX('IDX_JOB')
-----------------------
                      1

SQL> create index idx_job on emp(job);
create index idx_job on emp(job)
             *
ERROR at line 1:
ORA-00955: name is already used by an existing object


SQL>

 

Tom Kyte
December 01, 2006 - 9:19 am UTC

lose that stupid when others - stop using return codes, that is so 1980. Exceptions work, they work well, they stop you DEAD IN YOUR TRACKS, there is no chance that you will ignore the error. Just stop that coding practice, period.

DDL commits, you cannot commit in a function called from SQL unless you use an autonomous transaction BUT DON'T EVEN THINK ABOUT DOING THAT.

You have a simple function, just call it, don't even consider selecting it from something - that is just a waste.

and in sqlplus, exec is just short hand for:

begin procedure(...); end;


it is not really a "command", exec isn't, it is just a shortcut for begin/end. In your code, use BEGIN and END; and execute the anonymous block.

But, still question is unanswered…!!!

Suvendu, December 01, 2006 - 10:00 am UTC

I’m agreeing with you on each step you mentioned. What I did just for the test case to do it in short-cut way.
- I need to avoid WHEN OTHERS
- AUTONOMOUS TRANSACTION is NOT more applicable to functions as well as to triggers
- And, SQL*PLUS’s EXECUTE stands for BEGIN…END;

As I’m repeating my question again WHY dynamic SQL work (index dropped) when function called through BEGIN…END; but NOT through SELECT statement?

Once again, kindly correct me if I’m wrong and please clear my doubt.

Thanks,
Suvendu


Tom Kyte
December 01, 2006 - 10:15 am UTC

umm, re-read my answer. slowly :)

I already answered the "why"


....
DDL commits, you cannot commit in a function called from SQL unless you use an
autonomous transaction
........

got it - DDL commits, you cannot commit in a function called from SQL, you are trying to commit in a function called from SQL

that is why

YES, it works...

Suvendu, December 01, 2006 - 11:36 am UTC

Yes, I checked out...

I not only read slowly ( :-) ) but also thought your SQL is NOT my SELECT, there was my mistake....

Really, I'm proud to communicate with YOU....

Simply can THANK YOU for billion times.

Regards,
Suvendu


------------------

1  create or replace function fnc_drop_idx(p_index_name varchar2
  2  as
  3  PRAGMA AUTONOMOUS_TRANSACTION;
  4  l_index_name varchar2(30);
  5  begin
  6       select index_name into l_index_name
  7       from user_indexes
  8       where index_name=p_index_name;
  9       if l_index_name is not null then
 10               execute immediate 'drop index '||p_index_name;
 11       end if;
 12       return 0;
 13  exception
 14       when others then
 15               null;
 16       return 1;
 17* end;
SQL> /

Function created.

SQL> create index idx_job on emp(job);

Index created.

SQL> var x number
SQL> exec :x:=fnc_drop_idx('IDX_JOB');

PL/SQL procedure successfully completed.

SQL>
SQL> print x

         X
----------
         0

SQL> create index idx_job on emp(job);

Index created.

SQL>
SQL> select fnc_drop_idx('IDX_JOB') from dual;

FNC_DROP_IDX('IDX_JOB')
-----------------------
                      0

SQL>
 

Tom Kyte
December 01, 2006 - 12:45 pm UTC

man, I TOLD YOU NOT TO DO THAT.

so stop it. please, it is so wrong, don't use autonomous transactions, do not call plsql from sql needlessly like that.

exec immediate

sam, December 01, 2006 - 12:31 pm UTC

Tom:

Do you know what is wrong with exec immediate below:

FOR x in (select * from user_Tables order by table_name)
LOOP
htp.p('<TR>');
htp.p('<TD>');
htp.p(nvl(x.table_name,'&nbsp;'));
htp.p('</TD>');
htp.p('<TD>');

exec immediate 'SELECT count(*) from '||x.table_name
into t1_count;


(1): PLS-00103: Encountered the symbol "IMMEDIATE" when expecting one of the following:
(1): := . ( @ % ;
(1): The symbol ":= was inserted before "IMMEDIATE" to continue.

Tom Kyte
December 01, 2006 - 12:51 pm UTC

exec is a sqlplus'ism, it is short for execute in sqlplus and in sqlplus 

SQL> execute p

is just shorthand for:

SQL> begin p; end;
  2  /


you wanted to use EXECUTE IMMEDIATE, not "EXEC", as exec doesn't mean anything in the language called plsql 

dynamic

sam, December 01, 2006 - 2:14 pm UTC

Tom:

Now it says "invalid" table name in the select. How can that happen when I select table_name from user_tables?

DO you see anything wrong with the query?

Tom Kyte
December 01, 2006 - 9:44 pm UTC

got a full example?

remember stored procedures are compiled by default with definers rights - user_tables will be forever "fixed" - it won't be the person running the procedure or anything.

execute immediate

3360, December 01, 2006 - 9:18 pm UTC

This is not really a good idea anyway, but your execute immediate statement is wrong.

SQL> create or replace function f (p_tab in varchar2)
  2  return number as
  3    l_num number;
  4  begin
  5    execute immediate
  6      'begin select count(*) into :x from ' || p_tab || '; end;'
  7      using out l_num;
  8    return l_num;
  9  end;
 10  /

Function created.

SQL> select table_name, f(table_name) from user_tables;

TABLE_NAME                     F(TABLE_NAME)
------------------------------ -------------
INSTRUMENT                                 0
T1                                         8
T2                                         4
T3                                        10
DEPT                                       4
EMP                                       14
BONUS                                      0
SALGRADE                                   5
DUMMY                                      1
PERSON                                     0
ADDRESS                                    0

TABLE_NAME                     F(TABLE_NAME)
------------------------------ -------------
TELEPHONE                                  0
EMAIL                                      0
B                                          0
TEL_TYPE_LOOKUP                            0
TEST_PHONENUMBER                           1
HOLIDAY                                    1
T                                          5
T_ARC                                      2
INVOICE_TABLE                              3

20 rows selected. 

Tom Kyte
December 02, 2006 - 12:48 pm UTC

no, that isn't it - the syntax is better than your approach:

ops$tkyte%ORA10GR2> declare
  2          l_cnt number;
  3  begin
  4          execute immediate 'SELECT count(*) from '||'all_users' into l_cnt;
  5          dbms_output.put_line( l_cnt );
  6  end;
  7  /
31

PL/SQL procedure successfully completed.


 

COUNT

sam, December 03, 2006 - 12:57 pm UTC

om:

IT was failing at a table name "GROUP. Why does oracle allow that if it is RESERVED WORD.


SQL> select table_name from user_Tables where table_name = 'GROUP';

TABLE_NAME
------------------------------
GROUP

1 row selected.

SQL>select table_name,f_count(table_name) from user_Tables where table_name='GROUP'
                  *
ERROR at line 1:
ORA-06550: line 1, column 36:
PLS-00103: Encountered the symbol "GROUP" when expecting one of the following:
( <an identifier> <a double-quoted delimited-identifier>
table the
The symbol "<an identifier> was inserted before "GROUP" to continue.
ORA-06512: at "DEVADMIN.F_COUNT", line 5
ORA-06512: at line 1
 

Tom Kyte
December 03, 2006 - 1:19 pm UTC

you need to quote that identifier.


select count(*) from "GROUP"


so, select out:

'"' || table_name || '"'

instead of just table_name

Generic Routine to Store all parameters passed to a procedure to a Table

Sunil, March 31, 2007 - 10:35 pm UTC

Hi Tom

In our system we have a lot of Stored Procedures that are called by the Web Front-End and the SP's pass back ref cursors.

To debug issues it'll be very useful to have a utility procedure that we can put in each of the back-end procedures to store all the parameters that these procedures recieve in each execution.

We can always hard-code with something like :

SELECT 'P_USER_ID => ' || P_USER_ID || 
       ',P_SORT_FIELD => ' || '''' || P_SORT_FIELD || '''' 
       ... 
       into v_params 
FROM DUAL;   


and the store v_params into a table, but as parameters change this piece will have to change as well,

I was wondering by using the user_arguments table we can do this stuff dynamically, so that in each procedure i can add one (or 2 or 3) lines of standard code to do this which does not need to change as parameters change.

Will appreciate any ideas to help do this,

Thanks in advance,
Sunil



======= SOMETHING THAT I TRIED BUT DOES NOT WORK ======
It was easy enough to write a function that returns all
the parameters to a SP -

but when i execute immediate the result of the func below in the main proc it obviously does not work as the parameter name is inside the string !

function arg_sql
(p_package_name    IN VARCHAR2,
 p_proc_name       IN VARCHAR2
) return varchar2 as
CURSOR c  IS
 SELECT argument_name, data_type
 FROM user_arguments aa
 WHERE PACKAGE_NAME = p_package_name
   AND OBJECT_NAME = p_proc_name
   and in_out = 'IN'
 ORDER BY  position;

v_sql varchar2(4000);
v_start varchar2(5);
x number;
BEGIN
 v_sql := 'SELECT ';
 x := 0;
    FOR r IN c
    LOOP
  v_sql := v_sql ||
       case when x = 0 then  '''' else ''',' end ||
     r.argument_name || ' => '' || ' ||
     case
       when r.data_type = 'NUMBER' then r.argument_name
      else ''''''''' || ' || r.argument_name || ' || '''''''''
     end ||
     ' || ';
  x := x + 1;

  END LOOP;

     return v_sql || ''''' FROM DUAL';
END;

Tom Kyte
April 01, 2007 - 8:06 pm UTC

oh
my
gosh

man, you posted this purely to annoy didn't you?

NO BINDS IN PLACE ANYWHERE
SQL INJECTION ALL OVER THE PLACE
SCALABILITY & PERFORMANCE = OF COURSE NOT

man - why, why why, why!!!!!

I don't get it.

Please erase all of this code, immediately, throw it out, don't even try to salvage it, it is beyond hope.

If you wanted something like this, you could

create or replace type myArgArray as table of varchar2(4000)
/


and then create a procedure like:


create procedure log_it( p_proc_name in varchar2, p_args in myArgArray )
as
begin
forall i in 1 .. p_args.count
insert into log_table( some_time_stamp, proc_name, arg, i )
values ( systimestamp, p_proc_name, p_args(i), i );
end;
/



Or, investigate the use of DBMS_TRACE - a builtin package that logs lots of stuff.

your example gives me nothing ...

A reader, April 01, 2007 - 9:52 pm UTC

the code was not meant to be 'production' code - it was just to indicate use of user_arguments to determine the arguments at run time

in your example to call log_it i'll have to build p_args with the arguments that are passed into the proc - which is precisely i dont want to do


Tom Kyte
April 01, 2007 - 10:23 pm UTC

umm, you would just call it with formal parameters

eg:

create procedure p( x in int, y in date, z in varchar2 )
as
begin
log_it( 'p', myArgs( x, y, z ) );
....


you'll precisely have to do SOMETHING like that, else you - well - won't get the arguments.

Dynamic SQL

cmmurari, April 03, 2007 - 3:27 am UTC

Hi,

I have procedure that creating table using
execute immediate(vc_query);
volume of records 3 milliions.

Dynamic SQL ( CTAS) taking long time . When i run same normal "create table as select * from" statment from SQL*plus its finish in seconds.

I am not sure what causing this problem.

please help.

begin

vc_query := ' create table tb_entity_fund nologging as select /*+PARALLEL( A,8) PARALLEL(B,8) */
b.policy_id, b.item_id, b.product_id, pkg_ls_pm_pub_gl.f_get_entity_fund
(b.product_id,
a.money_id,
pkg_ls_pm_pub_gl.f_get_entity_type (b.policy_id, b.item_id),
pkg_ls_pty_ci.f_get_company_id (a.organ_id)
) v_entity_fund
from t_contract_master a, t_contract_product b
where a.policy_id = b.policy_id and a.liability_state > 0 ';

execute immediate(vc_query);

dbms_output.put_line(' tb_entity_fund created');

exception
when others then
dbms_output.put_line('Error while creating table tb_entity_fund :'||sqlerrm);
raise;
end;

Tom Kyte
April 03, 2007 - 11:11 pm UTC

trace it.

Substitute table name in DYNAMIC SQL

Reena, April 03, 2007 - 5:47 am UTC

create or replace PROCEDURE PAST_DUE_GET_DATA(mSession_no IN NUMBER,
AS_ON_DATE in date) AS

TYPE GLCurTyp IS REF CURSOR;
GL_cv GLCurTyp ;

sql_stmt varchar2(2000);
gl_tname varchar2(100);
ACCT_NO VARCHAR2(13);
ACC_NAME VARCHAR2(50);
ACC_MATURITY date;
ACC_GRADE VARCHAR2(2);
ACC_ARM_CODE VARCHAR2(3);

begin

SELECT 'CH_GLTRANS_'||TO_CHAR(AS_ON_DATE,'DDMMYY') INTO gl_TNAME FROM DUAL;


SQL_STMT:=' Select C.Account_No,C.SHORT_NAME, C.MATURITY,c.cust_grade,c.arm_code
From '|| gl_TNAME || ' c ' ||
' Where C.in_recd_no <> 2'||
' And C.book_bal < 0'||
' And Substr(C.Account_No,1,3) In ('||'''01A'''||')'||
' And Substr(C.Account_No,5,1) In '||'('||'''I'''||','||'''P'''||','||'''R'''||','||'''V'''||')';

EXECUTE IMMEDIATE SQL_STMT;


--- DBMS_OUTPUT.PUT_LINE(SQL_STMT);
OPEN GL_cv FOR sql_stmt;
LOOP
FETCH GL_cv INTO accT_NO,ACC_NAME,ACC_MATURITY,ACC_GRADE,ACC_ARM_CODE ;
insert into GL_PASTDUE VALUES
(accT_NO,ACC_NAME,ACC_MATURITY,ACC_GRADE,ACC_ARM_CODE,mSession_no );
EXIT WHEN GL_cv%NOTFOUND;
END LOOP;
CLOSE GL_cv;

COMMIT;

END;

Tom Kyte
April 04, 2007 - 9:32 am UTC

there are so many things so wrong about this...


How to use dynamic sql for dblink passed as input parameter

Asim, April 12, 2007 - 4:52 pm UTC

Hi Tom,

I am looking for your help on below problem -

I have a stored procedure which accepts three input parameters -
- v_feed_type
- v_dblink
- v_schema

Depending on the value passed query should run against the specific schemas.

Now I have a requirement of bulk insert something like this inside a stored procedure -

insert into error_table(acct_num, position, error_string)
select t1.acct_num,t1.position,decode(t1.home_phone, t2.home_phone, null, 'HomePhoneErr#')||
decode(t1.email_address, t2.email_address, null, 'EmailAddrErr#')||
decode(t1.city, t2.city, null, 'CityErr#') error_string
from v_schema.t1, t2@dblink
where t1.acct_num = t2.acct_num
and t1.feed_type = v_feed_type;


Now problem is I am unable to use "v_schema","v_dblink" and some constant values in dynamic sql.

I tried something like this -

CREATE OR REPLACE PROCEDURE test(feed_type IN VARCHAR2,
dblink IN VARCHAR2,
schema IN VARCHAR2)

AS

BEGIN

EXECUTE IMMEDIATE 'insert into error_table(acct_num, position, error_string)
select t1.acct_num,t1.position,decode(t1.home_phone, t2.home_phone, null, :1)||
decode(t1.email_address, t2.email_address, null, :2)||
decode(t1.city, t2.city, null, :3) error_string
from '||v_schema||'.t1, t2@'||v_dblink||' where t1.acct_num = t2.acct_num and t1.feed_type ='||v_feed_type using 'HomePhoneErr#','EmailAddrErr#','CityErr#';

END;


Could you please help me to find out what is going wrong?

Thanks,
Asim
Tom Kyte
April 13, 2007 - 12:30 pm UTC

what problem do you have. it is hard to comment on what is going wrong without, well, an ERROR MESSAGE or some description of the symptoms you are having.


How to use dynamic sql for dblink passed as input parameter

Asim, April 13, 2007 - 3:29 pm UTC

Hi Tom,
Sorry for confusing you. This is what I tried as below.

The procedure executes and gives me output as 'SUCCESS' but nothing gets inserted into error table although I am expecting it should.

CREATE OR REPLACE PROCEDURE TEST_COMPARE(P_SOURCE_FEED IN VARCHAR2,
P_DB_LINK IN VARCHAR2,
P_OUTPUT_STR OUT VARCHAR2)
AS

V_ERROR_STR VARCHAR2(100);
V_ERROR_CODE VARCHAR2(50);
V_ERROR_VALUE VARCHAR2(50);
V_ERROR_FLAG BOOLEAN := FALSE;
V_DB_LINK VARCHAR2(20) := NULL;
v_ks_link VARCHAR2(20);


BEGIN

V_DB_LINK := LOWER(P_DB_LINK);

v_test_link := 'TEST_D0';


EXECUTE IMMEDIATE 'insert into errors_test(account_num, role,num_mismatches,error_str)'||
' select tmp.account_num, tmp.role,num_chars(error_string,:2) ,tmp.error_string
from
(SELECT a.acct_num account_num,b.role role,
decode(trim(b.address_line_one),trim(a.addr_line1),null,:3)||
decode(trim(b.address_line_two),trim(a.addr_line2),null,:4)||
decode(b.postal_code||b.zip4_code,a.zip_cd,null,:5) error_string
FROM '
||v_ks_link||'.test_account b ,source_hist a
where b.acct_num = substr(a.acct_num,2)
and a.source_feed = :1
) tmp
where tmp.error_string is not null' using P_SOURCE_FEED, '#', 'AddrLine1#', 'AddrLine2#', 'Zip Code#';



EXECUTE IMMEDIATE 'insert into errors_test(account_num, role,num_mismatches,error_str)'||
' select tmp1.acct_num, 1 ,num_chars(tmp1.error_string,:2) ,tmp1.error_string
from
(SELECT a.acct_num acct_num,
decode(nvl(trim(tmp.addr1),0),nvl(trim(a.addr_line1),0),null,:3)||
decode(nvl(trim(tmp.addr2),0),nvl(trim(a.addr_line2),0),null,:4)||
decode(tmp.postal,substr(a.zip_cd,1,5),null,:5) error_string
from
(select ct.admin_contract_id acct_num, xa.addr_line1 addr1, xa.addr_line2 addr2,
xa.postal_code postal
from contact@'||V_DB_LINK||' ct ,
component@'||V_DB_LINK||' cc,
address@'||V_DB_LINK||' xa
where ct.contact_id = cc.contact_id
and cc.component_id = xa.instance_id(+)
and xa.entity(+) = :6
and xa.addr_tp_cd(+) = 1
and nvl(xa.end_dt(+),localtimestamp) >= localtimestamp
) tmp , source_hist a
where tmp.acct_num = substr(a.acct_num,2)
and a.source_feed = :1
) tmp1
where tmp1.error_string is not null' using P_SOURCE_FEED,'#','AddrLine1#', 'AddrLine2#', 'Zip Code#','CONTRACTCOMPONENT';


COMMIT;

P_OUTPUT_STR := 'SUCCESS';

EXCEPTION
WHEN OTHERS THEN
DECLARE
V_SQLCODE NUMBER := SQLCODE;
V_SQL_MSG VARCHAR(512) := REPLACE(SQLERRM, CHR(10), ',');
BEGIN
P_OUTPUT_STR := 'ERROR :'||'ERROR CODE ='||V_SQLCODE||', ERROR STRING = '|| V_SQL_MSG;
ROLLBACK;
END;
END;
/


Thanks,
Asim
Tom Kyte
April 13, 2007 - 7:18 pm UTC


if I see another "when others then HIDE THE ERROR FROM EVERYONE BECAUSE OF COURSE THEY NEED NOT REALLY ACTUALLY NOW WHAT FAILED...."

please erase that exception block. Just let errors you cannot fix (and hence are not really errors but expected conditions) propogate up and out - you don't need to rollback or anything, we do the right thing (statement level atomicity)

maybe you are wrong and nothing should be inserted?

How to use dynamic sql for dblink passed as input parameter

Asim, April 13, 2007 - 3:32 pm UTC

Hi Tom,
Sorry for one typo in the procedure.

Here is the corrected one.

Hi Tom,
Sorry for confusing you. This is what I tried as below.

The procedure executes and gives me output as 'SUCCESS' but nothing gets inserted into error table although I am expecting it should.

CREATE OR REPLACE PROCEDURE TEST_COMPARE(P_SOURCE_FEED IN VARCHAR2,
P_DB_LINK IN VARCHAR2,
P_OUTPUT_STR OUT VARCHAR2)
AS

V_ERROR_STR VARCHAR2(100);
V_ERROR_CODE VARCHAR2(50);
V_ERROR_VALUE VARCHAR2(50);
V_ERROR_FLAG BOOLEAN := FALSE;
V_DB_LINK VARCHAR2(20) := NULL;
v_ks_link VARCHAR2(20);


BEGIN

V_DB_LINK := LOWER(P_DB_LINK);

v_test_link := 'TEST_D0';


EXECUTE IMMEDIATE 'insert into errors_test(account_num, role,num_mismatches,error_str)'||
' select tmp.account_num, tmp.role,num_chars(error_string,:2) ,tmp.error_string
from
(SELECT a.acct_num account_num,b.role role,
decode(trim(b.address_line_one),trim(a.addr_line1),null,:3)||
decode(trim(b.address_line_two),trim(a.addr_line2),null,:4)||
decode(b.postal_code||b.zip4_code,a.zip_cd,null,:5) error_string
FROM '
||v_test_link||'.test_account b ,source_hist a
where b.acct_num = substr(a.acct_num,2)
and a.source_feed = :1
) tmp
where tmp.error_string is not null' using P_SOURCE_FEED, '#', 'AddrLine1#', 'AddrLine2#', 'Zip Code#';



EXECUTE IMMEDIATE 'insert into errors_test(account_num, role,num_mismatches,error_str)'||
' select tmp1.acct_num, 1 ,num_chars(tmp1.error_string,:2) ,tmp1.error_string
from
(SELECT a.acct_num acct_num,
decode(nvl(trim(tmp.addr1),0),nvl(trim(a.addr_line1),0),null,:3)||
decode(nvl(trim(tmp.addr2),0),nvl(trim(a.addr_line2),0),null,:4)||
decode(tmp.postal,substr(a.zip_cd,1,5),null,:5) error_string
from
(select ct.admin_contract_id acct_num, xa.addr_line1 addr1, xa.addr_line2 addr2,
xa.postal_code postal
from contact@'||V_DB_LINK||' ct ,
component@'||V_DB_LINK||' cc,
address@'||V_DB_LINK||' xa
where ct.contact_id = cc.contact_id
and cc.component_id = xa.instance_id(+)
and xa.entity(+) = :6
and xa.addr_tp_cd(+) = 1
and nvl(xa.end_dt(+),localtimestamp) >= localtimestamp
) tmp , source_hist a
where tmp.acct_num = substr(a.acct_num,2)
and a.source_feed = :1
) tmp1
where tmp1.error_string is not null' using P_SOURCE_FEED,'#','AddrLine1#', 'AddrLine2#', 'Zip Code#','CONTRACTCOMPONENT';


COMMIT;

P_OUTPUT_STR := 'SUCCESS';

EXCEPTION
WHEN OTHERS THEN
DECLARE
V_SQLCODE NUMBER := SQLCODE;
V_SQL_MSG VARCHAR(512) := REPLACE(SQLERRM, CHR(10), ',');
BEGIN
P_OUTPUT_STR := 'ERROR :'||'ERROR CODE ='||V_SQLCODE||', ERROR STRING = '|| V_SQL_MSG;
ROLLBACK;
END;
END;
/


Thanks,
Asim


DBLINK usage in dynamic sql

Asim, April 16, 2007 - 5:00 pm UTC

Hi Tom,
Looks like I got it figured out why it was not inserting any records.I suplied the values in the wrong order in "using" clause.That is why query was not finding any records.

Thanks for your help,
Asim

dynamic sql

Naveen, April 17, 2007 - 8:44 am UTC

declare
a number;
b varchar2(2) := 'c';
c varchar2(2) := 'XX';
d varchar2(2) := 'YY';
e varchar2(2);
stmt long;
begin
if a is null then
b := 'c';
else
b := 'd';
end if;
stmt:= 'select ''' || b || ''' from dual ';
dbms_output.put_line(stmt);
execute immediate 'select ''' || b || ''' from dual ' into e;
dbms_output.put_line(e);
end;


I hope this will solve ur problem
Tom Kyte
April 17, 2007 - 10:16 am UTC

if the problem was "our machines are not using 100% of the cpu and run too fast", that would help :)

what is up with this bit of code? what problem is it trying to solve? It introduces big bad problems in the form of literal SQL without binds - but???

Need your suggestion

Asim, April 19, 2007 - 7:48 pm UTC

Hi Tom,
I am here again seeking for your suggestion from you.

Here is the scenario -

I have a table lets say "A" which will have 100 million records approximately. All these records will be initially loaded
data which came from source system without any modifications.
These same 100 million records are loaded into destination systems by some other system after some kind of validations.
This process will be run only once when the system goes to production first time.

We are developing a process to compare these source records from this table with loaded records from target database
by sqls' and logging the defects wherever data mismatch is found.

Now till now it works good for initial loaded data.


Now problem starts here, we have another scenario like this -
daily we will be getting delta records for the existing records and updates will be applied to target database table
by other system as I mentioned.

We need a process to compare the latest records from target with whatever changes came in as delta from source
to verify if they have been applied correctly.

So we want to compare by using the same set of sqls used for initial loaded data. For that we need to apply the delta
changes received from source system in flat files on our table "A"(as above). We need some kind of tool which will
delete the records present into delta files from table "A" and insert the new records in delta files by sql loader
and there will be a delete trigger which will copy the deleted records into another history table so that
somebody can verify what was original record and what is changed.

By doing so, main table "A" will have all the latest records like master table and we can run same set of sql comparisons
with target system where changes have been applied.

But problem happened, as sql loader does not support this sort of functionality. So we thought of loading all the keys
for delta records daily into an intermediate table by sql loader utility and then delete those records from table "A"
while doing that delete trigger will copy those old records into history table. And once we are done with delete,
we can run sql loader to load those delta records into table "A" and truncate the intermediate table.

Problem in this approach is in the rollback segment as delete will take lots of space because daily in delta we will be
getting around 30 million records.

Could you please share your suggestion in this regard what should be the best approach to follow?

Thanks,
Asim

multiple values in a string causing stored procedure to hang even after commiting the data

Chandan Singh, May 23, 2007 - 8:13 am UTC

Hi Tom,

Just need your help again to know the strange behavior i am facing with one of the stored procedure.

I have one stored procedure that accepts multiple states('PB,KA,GJ') as one string and then execute one insert select statement in execute immediate command just like below statements.

v_sql1 := 'insert into t1 select * from t3 where state in '; 
v_state := '(''' || REPLACE (p_state, ',', ''',''') || ''')';
Execute Immediate v_sql1||v_state;

Insert t2 statement;

Update t2 statement

Update t2 statement;

Commit; 

Open refcursor for 'select * from t1,t2 where using joins';


I wasn't able to put the whole code, as it was quite lengthy & i just want to give the idea why this strange behaviour is happening so that's why given just a pseudo code.

After this insertion into table t1, there is one more insert & 2 update statements and at the end there is COMMIT. Please note that there is no commit in between update & insert statements. When i execute the stored procedure for one state(say 'PB'), it works fine, but when i ran the stored procedure for multiple states(say 'PB,KA,GJ') it inserts the data in table t1 and also executes the other statements and also commit it(as i can see this data thru another session) but problem is it never comes out and never shows the message "PL/SQL Procedure Completed Successfully" and window is completely blank. When i checked V$Session table's sql_hash_value it shows 0. Even v$transaction is not showing anything. I tried to kill the session, its shows the status in V$Session as killed but that session is still completely blank and nothing happens even after killing the session.

1. I don't know why this strange behavior is coming up when i executed the stored procedure for mutiple states?
2. Have you ever face this problem?

I hope i am clear what i want to say.

Thanks
Tom Kyte
May 23, 2007 - 8:53 am UTC

this is not clear at all.

you seem to have a long running query and got impatient, that is all. You killed it, you did not diagnose it, you did not use dbms_application_info to instrument it, you did not query v$ tables to see what it was doing.

I cannot really comment.

dynamic sql

Naveen, May 23, 2007 - 9:37 am UTC

Hi Nagaraju

Yours code can be rewritten without using bind variables as follows...
declare
a number;
b varchar2(10) := 'c';
c varchar2(10) := 'XX';
d varchar2(10) := 'YY';
e varchar2(10);
begin
if a is null then
b := '''c''';
else
b := '''d''';
end if;
execute immediate 'select ' || b || ' from dual ' into e;
dbms_output.put_line(e);
end;



Procedure performing Grants through dynamic SQL

Michel Boire, May 30, 2007 - 12:58 pm UTC

Hi Tom,

I wrote a Procedure to grant READ/WRITE on DIRECTORIES and Select on external tables. The Directory part works fine but when performing the grant on External tables, I get a
ERROR: ORA-06564: object MY_DIRECTORY does not exist
The statement causing this is
GRANT SELECT ON SH.MY_EXTERNAL_TBL TO MY_USER
The user performing the grant has CONNECT/DBA Roles and SELECT ANY DICTIONARY/SYSDBA system privilege.
.
CREATE OR REPLACE PROCEDURE "PRC_GRANT_USR"  IS  
V_SQL_STMT  VARCHAR2(200); 
V_CNT       NUMBER; 
V_USER_LIKE VARCHAR2(20); 
 
/* Cursor to get the usernames  
 Must be granted directly on a user, not a role */ 
CURSOR C_USERS IS 
SELECT 'GRANT '|| V_ROLE ||' TO ' ||USERNAME   
FROM DBA_USERS WHERE USERNAME LIKE V_USER_LIKE; 
 
/* Cursor create the grants statement for Users */ 
CURSOR C_GRANT_DIR_R IS 
SELECT 'GRANT READ ON DIRECTORY SYS.' ||DIRECTORY_NAME ||' TO ' || USERNAME 
FROM DBA_DIRECTORIES, DBA_USERS  
WHERE USERNAME LIKE V_USER_LIKE; 
 
CURSOR C_GRANT_DIR_W IS 
SELECT 'GRANT WRITE ON DIRECTORY SYS.' ||DIRECTORY_NAME ||' TO '|| USERNAME  
FROM DBA_DIRECTORIES, DBA_USERS  
WHERE USERNAME LIKE V_USER_LIKE; 
 
CURSOR C_GRANT_EXT_TBL IS 
SELECT 'GRANT SELECT ON '||OWNER|| '.' ||TABLE_NAME ||' TO '|| USERNAME  
FROM DBA_EXTERNAL_TABLES, DBA_USERS  
WHERE USERNAME LIKE V_USER_LIKE; 
 
BEGIN 
 
V_USER_LIKE:='MY%'; 

 
/* Grant Directory Read to Users that matches the input string */  
OPEN C_GRANT_DIR_R; 
    FETCH C_GRANT_DIR_R INTO V_SQL_STMT; 
    WHILE C_GRANT_DIR_R%FOUND LOOP     
      BEGIN 
         EXECUTE IMMEDIATE V_SQL_STMT; 
         EXCEPTION 
         WHEN OTHERS 
         THEN DBMS_OUTPUT.PUT_LINE('ERROR: '||SQLERRM ||CHR(10)||'ON STATEMENT: '||V_SQL_STMT); 
      END;   
      FETCH C_GRANT_DIR_R INTO V_SQL_STMT;  
   END LOOP;  
/* Close the cursor */     
CLOSE C_GRANT_DIR_R;  
 
 
/* Grant Directory Write to Users that that matches the input string */  
OPEN C_GRANT_DIR_W; 
    FETCH C_GRANT_DIR_W INTO V_SQL_STMT;  
    WHILE C_GRANT_DIR_W%FOUND LOOP     
      BEGIN 
         EXECUTE IMMEDIATE V_SQL_STMT; 
         EXCEPTION 
         WHEN OTHERS 
         THEN DBMS_OUTPUT.PUT_LINE('ERROR: '||SQLERRM ||CHR(10)||'ON STATEMENT: '||V_SQL_STMT); 
      END;   
      FETCH C_GRANT_DIR_W INTO V_SQL_STMT; 
   END LOOP;  
/* Close the cursor */     
CLOSE C_GRANT_DIR_W;  
 
/* Grant Select on External Tables to Users that matches the input string */  
OPEN C_GRANT_EXT_TBL; 
    FETCH C_GRANT_EXT_TBL INTO V_SQL_STMT;  
    WHILE C_GRANT_EXT_TBL%FOUND LOOP     
      BEGIN 
         EXECUTE IMMEDIATE (V_SQL_STMT); 
         EXCEPTION 
         WHEN OTHERS 
         THEN DBMS_OUTPUT.PUT_LINE('ERROR: '||SQLERRM ||CHR(10)||'ON STATEMENT: '||V_SQL_STMT); 
      END;   
      FETCH C_GRANT_EXT_TBL INTO V_SQL_STMT;  
   END LOOP;  
/* Close the cursor */     
CLOSE C_GRANT_EXT_TBL;  
 
  
END;
/

.
I can see that my user was indeed granted read/write on the directory.

If I execute through SQL plus via an anonymous block, it works. Why can't I grant on external tables via the proc?

Regards,

Michel
Tom Kyte
May 30, 2007 - 4:02 pm UTC

roles do not count
http://asktom.oracle.com/tkyte/Misc/RolesAndProcedures.html

you might be able to use invoker rights - but only if the user calling this calls it directly (not from some other definer rights procedure where the roles would have been disabled)

Am I having the same issue?

Don, June 08, 2007 - 4:03 pm UTC


Earlier in this thread you replied:

"Followup May 20, 2005 - 10am US/Eastern:
well, see the other place you posted this, but they are apples and oranges (z in one, not the
other)

9i rejects the select b from ( select b from ....
and wants select * from ( select b from ....

in 8i, if you make the queries the same -- they behave the same. it is an issue, and as mentioned
on the OTHER place you put this, please contact support."


I think my problem relates to this, it is the closest I have been able to find, but I am a little unsure. I have an Oracle query that is running fine in our Production environment as well as in Model Office. However, it encounters a problem in the Development environment.

The procedure consists of a cursor that is looped through, inserting rows for a report to display. If the procedure is executed in development through SQL*Plus, everything works fine. However, if executed from our application, when the procedure is run, the cursor returns no data and no report rows are created.

We have compared the code in all environments - the same. Oracle versions - the same. Yet in that one environment the procedure will only work when executed from SQL*Plus.

We have also put debug statements in the development code, displaying all the parameters passed in and used as variables for the cursor. The values are exactly the same whether the procedure is executed from SQL*Plus or from our application. Yet, in one case it finds data, and in the other it doesn't.

I am wondering if my problem could be caused by the same issue (bug?) you referenced in your reply to her since my cursor has a similar format to the snippet you gave and we are also using Oracle 9.2

ie.
CURSOR XLT_TIME IS
SELECT
  :
  :
FROM
  (SELECT 
    :
    :
  FROM 
    (SELECT ...) SXLT,   
    (SELECT ...) XLT  
  WHERE                                               
    :
    :    
  UNION                                                
  SELECT 
    :
    :
  FROM 
    e,
    (SELECT ...) SXLT,   
    (SELECT ...) XLT  
  WHERE 
    :
    :) rpt_data
GROUP BY
  :
  :


However, if it is the same issue, I am not sure why it would be working in the other environments unless a recent patch introduced the problem. This is driving me batty. Do you have any pointers for steps I can use to try tracking down such an odd issue? Is this something I should also contact support with?


OK

A reader, August 27, 2007 - 6:47 am UTC

Hi Tom,
I have these doubts

1)Do DDL Operations over a DB Link supported in latest version of Oracle?

2)When to use execute_and_fetch procedure of dbms_sql package
Tom Kyte
September 04, 2007 - 11:46 am UTC

1) it is highly unlikely that this will ever be supported - it doesn't really make sense (if you ask me) and the issues with the COMMIT - ugh.

dbms_job@remote_site - schedule the DDL to be executed

2) you can call it all of the time, just makes the code a little different looking, normally you:

execute
while ( fetch_rows > 0 )
loop
  ...
end loop;


with execute_and_fetch, it would look more like:

n := execute_and_fetch
while( n > 0 )
loop
    ...
    n := fetch_rows
end loop


you would be tempted to use execute and fetch for a query that retrieves data over a database link to reduce the number of round trips, especially if you anticipated a single row (but then, I would use native dynamic sql in 10g and above - in 9i, if the query was only executed once or a few times I would use native dynamic sql, restorting to dbms_sql only when I wanted to execute the query many times)

selecting columns names

Ravi, October 09, 2007 - 1:53 am UTC

what if tom if i have a table with 200 columns and i need to select 199 columns. is there a way out other than writing names of all 199 columns in the select statement?



Variable number of columns being returned from dynamic query

Vinay Chandrakant, November 06, 2007 - 11:04 am UTC

Hi Tom,

I read the posts above - I have a question regarding handling dynamic sql statements where I do not know in advance, how many columns the statement will return. My dynamic statement could be:

select col1,col2,sum(qty) from tablename group by rollup(col1,col2);

During a subsequent execution it might be:

select col1,count(*) from tablename group by col1;

In general, the resultset will contain one number field and one or more than one varchar2 fields.. I would like to know how I can handle this scenario - how do I define the record I collect the results into?

I think this could be done using dynamic PL/SQL (exec. imm. 'begin ... end;'), but in that case how could I pass the result to the outer code block?

Thanks,
Vinay

Tom Kyte
November 06, 2007 - 3:05 pm UTC

if you are in plsql entirely, you just use dbms_sql, it has a describe cols API that tells you how many columns there are, their datatypes and their names.

Just my 2 cents

Barry Chase, November 07, 2007 - 12:01 am UTC

Following some material I found here at AskTom, I put together a sample whitepaper and demo download that details variable column selection which can differ from one request to another. I have posted the whitepaper at my site for those interested as well as at ITToolbox. Working to get it posted to plnet.org as well.

http://wiki.ittoolbox.com/index.php/Oracle_Dynamic_SQL_-_Multi-Parameter


How about this code...

A reader, November 09, 2007 - 1:09 pm UTC

create or replace function robust_function (v_ename in varchar2, v_jobdesc in varchar2) return number is
begin
for i in 1..10000 loop
execute immediate 'insert into employee (ename, jobdesc) values (' || v_ename || ',' || v_jobdesc || ')';
commit;
end loop;
exception when others then
null;
end robust_function;
Tom Kyte
November 09, 2007 - 1:11 pm UTC

as an example of precisely what "not to do", that is perfect.

To A reader from Calcutta

SeánMacGC, November 09, 2007 - 2:18 pm UTC

Thanks for the laugh! Are you sure the 'robust' shouldn't have been 'rubbish'? :o)

LOL!

A reader, November 13, 2007 - 1:40 am UTC

To 'A reader from Calcutta' ... Laughed out loud on that one...

To SeánMacGC,
I think the name is the cherry on the cake ...

ODCI for 'method 4' dynamic sql (?)

A reader, November 16, 2007 - 4:06 pm UTC

Hi Tom,

I have a question regarding implementation of a function where the inputs are not known at compile-time. I mean - nothing is known. Not the number of inputs, the types of inputs, the resultset structure, the table name.. I spent a while creating a piece of code that works by dynamically generating anonymous pl/sql code blocks at runtime, also creating the required record structures etc. Then I came across the following article:

http://www.oracle-developer.net/display.php?id=422

He uses ODCI routines ODCITableDescribe, ODCITablePrepare, ODCITableStart and ODCITableFetch. The anydata and anydataset have also been used.

I compiled and tried this and it works perfectly. However types of the form SYSTT..... are created with each execution of the function. Is there any way to 'clean up' after completion?

In addition - is there any pitfall you see in the approach - anything that I might need to be wary of? I'm asking because ODCI is something I am not very familiar with - is this above approach something YOU would use in your 'method 4' requirements?

On a more general note: Do you know of any good books on this ODCI concept (by any chance are you writing/planning to write one??!)..

Thanks!

variable schema owner in dynamic sql in stored procedure

A reader, July 23, 2008 - 3:27 pm UTC

Tom,
I'm trying to execute a stored procedure which calls a second stored procedure owned by a different user. The issue is that the owner of the second procedure is not known until execution time (the name of the stored procedure is fixed). For instance, here is my code:
conn / AS sysdba

CREATE USER SPOWNER IDENTIFIED BY SPOWNER DEFAULT tablespace admin;
CREATE USER TABOWNER IDENTIFIED BY TABOWNER DEFAULT tablespace admin
   quota unlimited ON admin;
CREATE TABLE TABOWNER.mytable (col1 NUMBER);
INSERT INTO TABOWNER.mytable VALUES (1);
COMMIT;

Create or replace PROCEDURE TABOWNER.Truncate_mytable 
 (p_table_name  VARCHAR2)
IS 
  v_dyn_sql       VARCHAR2(1000);
  v_table_name    VARCHAR2(30) := p_table_name;
BEGIN
   v_dyn_sql := 'truncate table ' || v_table_name;
   execute IMMEDIATE v_dyn_sql;
END;
/

GRANT execute ON TABOWNER.truncate_mytable TO SPOWNER;

Create or replace PROCEDURE SPOWNER.Truncate_table 
 (p_table_owner VARCHAR2,
  p_table_name  VARCHAR2)
IS 
  v_dyn_sql       VARCHAR2(1000);
  v_table_owner   VARCHAR2(30) := upper(p_table_owner);
  v_table_name    VARCHAR2(30) := upper(p_table_name);
BEGIN

-- This statement works, if we know who owns the table to be truncated.
--   TABOWNER.truncate_mytable (v_table_name);

   v_dyn_sql := v_table_owner || '.truncate_mytable (''' || v_table_name || ''')';
   execute IMMEDIATE v_dyn_sql;

END;
/

CREATE USER app IDENTIFIED BY app;
GRANT CREATE session TO app;
GRANT SELECT, insert, DELETE ON TABOWNER.mytable TO app;
GRANT EXECUTE ON SPOWNER.TRUNCATE_TABLE TO app;

conn app/app
SELECT * FROM TABOWNER.mytable;



I created SPOWNER to own the "outer stored procedure", and TABOWNER owns the "inner stored procedure". I have app being the user to execute the "outer stored procedure". The problem is, since I want multiple "TABOWNER" schemas, I don't know which one will be passed to SPOWNER.TRUNCATE_TABLE until runtime. As a result, I get:

SQL> exec SPOWNER.truncate_table ('TABOWNER','MYTABLE');
BEGIN SPOWNER.truncate_table ('TABOWNER','MYTABLE'); END;

*
ERROR at line 1:
ORA-00900: invalid SQL statement
ORA-06512: at "SPOWNER.TRUNCATE_TABLE", line 14
ORA-06512: at line 1



Is there anyway to do something like this?

The need for all of this is because of new security requirements in my organization which won't allow for direct grant of system privileges to schemas -- only privileges through roles.
Tom Kyte
July 24, 2008 - 10:42 am UTC

beware sql injection, you want to use dbms_assert or a query on user_objects to verify they send you simply "a table name in the current schema"


execute immediate '
begin ' ||
v_owner || '.truncate_table( :x );
end;'
using v_table_name;

Thank you!!!

A reader, July 24, 2008 - 2:35 pm UTC

That code worked wonderfully. As for the SQL injection comment, I had only provided a snippet of our actual case. We actually test for validity of the schema owner name, and the validity of the table name, and the ability of the user calling this procedure to actually be allowed to delete from the table. If any of those fail, we never even get to call the secondary stored procedure.

dynamic query

dm, September 16, 2008 - 1:05 pm UTC

That is wonderful expplaination ...but this not working may I know what is the issue ..

Thanks in advance



Create or replace procedure test11
IS
v_sql varchar2(1000):=NULL;
Begin
dbms_output.put_line('Inside !');
v_sql := ('create table dmqq(col1 varchar2(10))');
execute immediate v_sql;
dbms_output.put_line('Outside !');
exception
WHEN others then
dbms_output.put_line('ERROR !');
END;
Tom Kyte
September 16, 2008 - 11:36 pm UTC

erase everything from "exception" up to but not including "end;" and you'll be very enlightened.

the first thing you should learn from it will be "when others is evil, stop using it"


after that you will learn the real cause of the error which is probably insufficient privileges and the reason for that is:

http://asktom.oracle.com/tkyte/Misc/RolesAndProcedures.html

dynamic query

DM, September 17, 2008 - 6:35 am UTC

What a Wonderful explaination ..WOW !
May I have ur thoughts on this ....

I have to create the table dynamically like

Table_A(col1 varchar2(10),col2 varchar2(10),col3 varchar2(10),col4 varchar2(10),col5 varchar2(10)...........);
Table_B(col1 varchar2(10),col2 varchar2(10),col3 varchar2(10),col4 varchar2(10),col5 varchar2(10)...........);
Table_C(col1 varchar2(10),col2 varchar2(10),col3 varchar2(10),col4 varchar2(10),col5 varchar2(10)...........);
Table_D(col1 varchar2(10),col2 varchar2(10),col3 varchar2(10),col4 varchar2(10),col5 varchar2(10)...........);
.
.
This Table_A,Table_B,Table_C.... are values in under some other table.col33.....
say TABLE22(col1,.,col2..,..,col33,..)
like
col1 col2 col3. ....ds_nm.....
------------------------------------------
111 222 3333 TABLE_A
TABLE_B
TABLE_C

AND columns of these TABLE_A,TABLE_B,TABLE_C..with datatype name/sizes........are also the value in the fields of some other table..

like

var_sas_dt_ds var_leng var_nm col33
---------------------------------------------
varchar2 10 xse2 sadsa
varchar2 10 xse3 sadsa
varchar2 10 xse4 sadsa
varchar2 10 xse5 sadsa
varchar2 10 xse6 sadsa
varchar2 10 xse7 sadsa
.........and so on .....

I am doing like this but it is not working ...


Begin

for i in (select ds_nm
from sp_ds t
where sp_id = 84
and rownum <= 3)
loop
for j in (select sdv.var_nm,
sdv.var_sas_dt_desc,
sdv.var_leng
from spec_ds_vr_dl_v sdv
where sp_ds_id IN
(select t.sp_ds_id
from sp_ds t
where sp_id = 84
and rownum <= 3))
loop
execute immediate 'CREATE TABLE dmtst_'||i.ds_nm ||'('||j.var_nm||' '||j.var_sas_dt_ds||'('||j.var_leng||'))';
end loop;
end loop;
End;




Thanks in advance
DM -IND
Tom Kyte
September 17, 2008 - 9:27 am UTC

why do you HAVE to create a table dynamically, this is a really bad idea. Doing DDL in production is a horrible, bad, to be avoided at all cost idea.

Why are you doing this? What is the goal (do not say "goal is to create a table", do tell us WHY you feel compelled to create this table on the fly)

dynamic query

DM, September 17, 2008 - 9:34 am UTC

Idea is to create the schemas on fly on the basis of the dataset which inturn consists of the variables which are associated with some reports or study, of the observation or R&D.which inturn will be creatinf views dynamically.......and we have to add the PK to the above table dynamically may be composite
ur thoughts are welcome...
Tom Kyte
September 17, 2008 - 11:42 am UTC

just use SQL and queries. I don't see any need to create schemas on the fly.

if you design a data model, you can then just use that data model.


dynamic query

DM, September 19, 2008 - 5:42 am UTC

thanx for ur thoughts !

How I would alter the table to add composite primary key dynamically?

I am creating the table dynamically !

Any thoughts !

Thanks in Advance

DM
Tom Kyte
September 19, 2008 - 8:06 am UTC

do not create tables dynamically!
do not create primary keys dynamically!
those are my thoughts!


and you apparently already know how to do dynamic sql from plsql, so you should be able to answer your own question here!

dynamic sql

DM, September 22, 2008 - 6:57 am UTC

Your Thoughts are welcome but I am not able to figure out ...how to dynamically create composite primary key ....
Any help is welcome


Tom Kyte
September 23, 2008 - 12:26 pm UTC

umm, why not?

you know the syntax for creating a primary key right?
you have some logic that identifies what the primary key should be right? (please say yes, if you don't, you are not ready to even begin writing code)

Since you know the syntax
And since you know the logic

what could possibly be the stumbling block here???? I'm confused - why is this a problem/problematic - I cannot imagine what would be stopping you from proceeding with this really bad bad idea.

dynamic query

DM, September 22, 2008 - 9:48 am UTC

Thanks Tom...
Now I am able to figure it out on my own.
;-)

dynamic query

DM, September 30, 2008 - 1:06 pm UTC

Hi Tom
I am not able to execute the Followiing Proc

Create or replace procedure test11
IS
v_sql varchar2(1000):=NULL;
Begin
v_sql := ('create table dmqq(col1 varchar2(10))');
execute immediate v_sql;
END;

Now I am aware of the actual Problem behind it but I want to know specifically what are all roles privileges my proc requires ...or user who is exec this proc..and how I will get access to them...

Thanx in Advance

Tom Kyte
September 30, 2008 - 1:47 pm UTC

first, don't do ddl in a stored procedure. just don't (oh, I've already said that, well - they do say repetition is the key to success so... )

do not do this....



but, read this:
http://asktom.oracle.com/tkyte/Misc/RolesAndProcedures.html

you need no roles
you would need

o create session (to log in)
o create procedure (to create procedure)
o create table (to create the table)
o the ability to allocate space in your default tablespace (a quota or unlimited quota)

and then you could implement this bad idea.

Is binding required in stored procedure

A reader, October 15, 2008 - 12:23 pm UTC

Hi Tom -
Can you please tell me if binding is explicity required in stored procedures or if there is implicit binding by Oracle in a database stored procedure. Examples are below, both procedures do the same thing.

This stored procedure has binding:

CREATE OR REPLACE PROCEDURE P_TEST (v_ID NUMBER)
 is
 v_sql_statement       CLOB    := null;
 v_cursor_id      number    := null;
 v_rows_processed      number    := null;
   v_str_name varchar2(500) := null;


begin
  begin

    v_cursor_id := dbms_sql.open_cursor;
    v_sql_statement :=
      'select
        NAME as NAME
  from TEST
  where
  id = :v_id';

    dbms_sql.parse(v_cursor_id,v_sql_statement,dbms_sql.native);

    dbms_sql.define_column(v_cursor_id, 1, v_str_name,500);

    dbms_sql.bind_variable(v_cursor_id, 'v_id', v_id);
    v_rows_processed := dbms_sql.execute(v_cursor_id);

    if dbms_sql.fetch_rows(v_cursor_id)>0 then
       dbms_sql.column_value(v_cursor_id, 1, v_str_name);

     end if;

 dbms_sql.close_cursor(v_cursor_id);

 exception
  when others then
   null;
  end;

 end;
/


This stored procedure doesn't have binding:

CREATE OR REPLACE PROCEDURE P_TEST (v_ID NUMBER)
 is

 v_sql_statement       CLOB    := null;
 v_cursor_id      number    := null;
 v_rows_processed      number    := null;
   v_str_name varchar2(500) := null;

begin
     begin

        select nm_lst 
         into v_str_name
        from person
        where
        id_prsn = v_id;


    exception
        when no_data_found then
            null;
     end;
 end;

/

Tom Kyte
October 15, 2008 - 5:52 pm UTC

if you use static sql in plsql - you are always binding 100% correctly.

if you use dynamic sql in plsql - you must take care to bind yourself.


ugh, you disappoint me no end with this:

    exception
        when others then
            null;
     end;



why bother writing any of the code above the exception? It is useless code, just delete it. With that when others then null, you are saying "i don't care of 0%, 10%, 50%, 100% of the code runs", so if it can fail sometimes it can always fail.

Please don't say "but this is just an example", doesn't matter, I'll say it again - when others then null is a bug in your code. stop it please!


catch ONLY the exceptions you can HANDLE, ignore ALL OTHER EXCEPTIONS - you don't know what to do when they happen!!!


Your second procedure does use bind variables, you are referencing a variable right there in the query!


The rule:

use static sql in plsql
never use when others then null - never
use dynamic sql only when you have NO OTHER CHOICE and then be very careful to bind properly!

How to write Dynamic Dynamic Bind and ref cursor

Nags, October 17, 2008 - 6:20 pm UTC

I have a requirement and I am not sure how it can be done. I did a some research but was not able to figure out.

This is my code snippet

CREATE OR REPLACE PACKAGE BODY RPT_PKG AS
-- AccesslogReport
PROCEDURE ACCESSLOG_RPT (
pFACILITYID IN VARCHAR2
, pMEMBERGIVENNAME IN VARCHAR2
, pMEMBERSURNAME IN VARCHAR2
, pPATIENTGIVENNAME IN VARCHAR2
, pPATIENTSURNAME IN VARCHAR2
, pACCESSLOGCSR OUT ACCESSLOGCSR
) IS
STATEMENT VARCHAR2(4000);
BEGIN
STATEMENT := 'SELECT MEMBERSURNAME, MEMBERGIVENNAME,
LOGDATE, ACTIONINFO
FROM ACCESSLOG
WHERE FACILITYID = :1
AND (MEMBERSURNAME LIKE :2 OR :3 IS NULL)
AND (MEMBERGIVENNAME LIKE :4 OR :5 IS NULL)
AND (PATIENTSURNAME LIKE :6 OR :7 IS NULL)
AND (PATIENTGIVENNAME LIKE :8 OR :9 IS NULL);
OPEN pACCESSLOGCSR FOR STATEMENT USING pFACILITYID,
pMEMBERSURNAME, pMEMBERSURNAME,
pMEMBERGIVENNAME, pMEMBERGIVENNAME,
pPATIENTSURNAME, pPATIENTSURNAME,
pPATIENTGIVENNAME, pPATIENTGIVENNAME;
END ACCESSLOG_RPT;
END RPT_PKG;

In this all the NAME parameters can have null values. Based on the NAME fields I need to add or not add criteria into the where clause. But, since I cannot have dynamic binds to the OPEN pACCESSLOGCSR I am not able to add or remove criteria from the where clause. So I have to keep adding "or :bindVariable is NULL" to the where clause.

First is this doable, having variable bind variables? if yes how?

Need help with this

Srp, October 22, 2008 - 8:34 am UTC

I have a Proc which return's a dynamic result set based on some input parameters. The Number of columns returned along with their types would depend on the inputs passed to be proc.

Lets say ref1 is the refcursor

v_select0 := 'SELECT * from emp';
--
v_select00 := 'SELECT e.*, d.* from emp, dept d where e.deptno = d.deptno';

IF <Input> = <Some input>
OPEN ref1 for v_select0;
ELSE
OPEN ref1 for v_select00;
.. And so on
END;

Thats a simple example of what i mean
How do i write a test script to read such a result set.
( The only way i know to test such a proc is to run this in PL-SQL developer) Please Help me out with an alternative
Tom Kyte
October 22, 2008 - 9:07 am UTC

sqlplus can do that easily

ops$tkyte%ORA10GR2> variable x refcursor
ops$tkyte%ORA10GR2> create or replace procedure p( p_cur in out sys_refcursor )
  2  as
  3  begin
  4          if ( mod(to_number(to_char(sysdate,'sssss')),2) = 0 )
  5          then
  6                  open p_cur for 'select * from dual';
  7          else
  8                  open p_cur for 'select * from all_users where rownum = 1';
  9          end if;
 10  end;
 11  /

Procedure created.

ops$tkyte%ORA10GR2> set autoprint on
ops$tkyte%ORA10GR2> exec p(:x)

PL/SQL procedure successfully completed.


D
-
X

ops$tkyte%ORA10GR2> exec dbms_lock.sleep(0.5)

PL/SQL procedure successfully completed.

ops$tkyte%ORA10GR2> exec p(:x)

PL/SQL procedure successfully completed.


USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYS                                     0 30-JUN-05

dynamic ref cursors

Srp, October 23, 2008 - 3:06 am UTC

Thanks Tom,
That was very helpful.

Can i in any ways write a script where in i can dynamically fetch from such a Ref Cursor.

Tom Kyte
October 23, 2008 - 1:33 pm UTC

I do not know what you mean.

A (sqlplus) script does not fetch explicitly, you print out result sets, it does the rest.

can you be more detailed?

Who's the best option

Filipe Almeida, October 23, 2008 - 7:02 am UTC

Hi Tom,

I have the next code to execute a lot(maybe 49000) of DML scripts (Insert and Delete) that are stored in a field table

PROCEDURE p1 (
p_rowid IN ROWID,
p_message CLOB
)
IS
lc_cursor PLS_INTEGER;
ln_retval NUMBER;
BEGIN
lc_cursor := DBMS_SQL.open_cursor;
DBMS_SQL.parse (lc_cursor, p_message, BMS_SQL.native);
ln_retval := DBMS_SQL.EXECUTE (lc_cursor);
DBMS_SQL.close_cursor (lc_cursor);
end;

This is efficient? I have poor performance in this procedure. My p_message variable must be a CLOB because the size of the DML. It can be more than 32k.

Thanks in advance
Tom Kyte
October 23, 2008 - 1:44 pm UTC

if the clob is more than 32k, this will fail - in 11g it could succeed, in 10g and before, it will fail.

in 10g you would have to use an array, like this:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:7032414607769#238355700346849272

the performance has nothing to do with dynamic sql - the performance will be constrained by the STATEMENTS themselves.


If you execute 49,000 statements and they take 1 second apiece (for example, maybe some take more and some take less) - about 13 hours later, you might be done.


you could just

execute immediate to_char(p_message);


in 10g (just execute immediate p_message; in 11g)

but - it'll be about the same, most of your time will be spent executing the sql one would think...

dynamic fetch

Srp, October 24, 2008 - 3:53 am UTC

I want a script where in i can fetch from the dynamic result set.
i dont want to display all the rows fetched from the resultset, but want to print back a few rows and some few important columns.
Say the result set fetched some 5000 rows, and for each result set there are some common columns which i need to print for 10 - 20 rows.. When i fetch i need to know all the columns before hand ( Or dont i ).
In this case i dont know the columns .. how do i fetch the result set.
Tom Kyte
October 24, 2008 - 1:17 pm UTC

you need to write a program, sqlplus isn't going to be sufficient. Pick the 3gl language of your choice - or in 11g, you can convert a refcursor into a dbms_sql cursor and do it procedurally as well.

Zlatko Sirotic, October 24, 2008 - 1:29 pm UTC

Srp, maybe this can help:

"Dynamic Ref Cursor with Dynamic Fetch - An 11g Version"
http://www.toadworld.com/Knowledge/DatabaseKnowledge/DatabaseTips/OracleTips/tabid/311/Default.aspx
Categories: PL/SQL 2007


Regards,
Zlatko Sirotic

Efficiency of dynamic SQL

Vijay, November 21, 2008 - 9:19 am UTC

Tom,

Do you see any issues(parsing and etc) in using dynamic SQL over non-dynamic. For eg,

Between

execute immediate 'insert into table_1 select * from table_1';

and

insert into table_1 select * from table_1;

which one is preferred/recommended.

Tom Kyte
November 24, 2008 - 2:21 pm UTC

static sql is always preferred.

not because of performance (all sql in Oracle is dynamic sql under the covers - ALL OF IT).

because syntax and permissions are checked at compile time (easier to code, maintain, debug).

because dependencies can be set up, maintained by the server, reported on.


Only use dynamic sql when your back is to the wall and there is no other practical way to accomplish your goal (that is - very very rarely)

Listing Bind Variables

bc, November 24, 2008 - 6:53 pm UTC

Tom,

We have a process that parses and loads queries into a clob. Parsing is done using dbms_sql.parse.

After parsing the query Is there a easy way or method that I could use to identify all bind variables used in that specific query?

The table that holds the queries is defined as

create table user_query
(
query_id number(10) not null,
query clob
);

The function that loads and parses query in an sql file is as follows

function load_query
(
p_directory varchar2,
p_file_name varchar2,
p_query_name varchar2
)
return number is

l_query_id number(10);
l_destination clob;
l_source bfile := bfilename( p_directory, p_file_name );
l_dst_offset number := 1 ;
l_src_offset number := 1 ;
l_lang_ctx number := dbms_lob.default_lang_ctx;
l_warning number;
l_cursor integer := dbms_sql.open_cursor;

begin
insert
into user_query ( query_id,
query )
values ( query_s.nextval,
empty_clob() )
returning query_id,
query
into l_query_id,
l_destination;

dbms_lob.fileopen( l_source, dbms_lob.lob_readonly );

dbms_lob.loadclobfromfile( dest_lob => l_destination,
src_bfile => l_source,
amount => dbms_lob.getlength(l_source),
dest_offset => l_dst_offset,
src_offset => l_src_offset,
bfile_csid => dbms_lob.default_csid,
lang_context => l_lang_ctx,
warning => l_warning );
dbms_lob.close ( l_source );

begin
select query
into l_destination
from user_query
where query_id = l_query_id;
exception
when others then
raise;
end;

begin
dbms_sql.parse( l_cursor, clob_to_row(l_destination), dbms_sql.native );
exception
when others then
raise;
end;
return l_query_id;
exception
when others then
raise;
end load_query;

The clob_to_row function, reads the clob and returns varchar2.

So, if the query in the clob looks like

select employee_name
from employee
where employee_id = :x
and department = :y

Is there a way to easily identify the names of the bind variables other than looking for ":" followed by a word.

Appreciate your help.

Thanks

BC




Tom Kyte
November 24, 2008 - 7:57 pm UTC

it is a bit brute force:

function get_binds( p_stmt  in varchar2 ) return dbms_sql.varchar2_table
as
    l_binds      dbms_sql.varchar2_table;
    l_ch         varchar2(2);
    l_str        varchar2(255);
 l_stmt   long default p_stmt;
 l_added      long default ':';

 n    number;
 m            number;
 o            number;
begin
 loop
  n := nvl( instr( l_stmt, '/*' ), 0 );
  exit when (n=0);
  m := nvl( instr( l_stmt, '*/', n+1 ), length(l_stmt) );
  if ( m = 0 ) then m := length(l_stmt); end if;
  l_stmt := substr( l_stmt, 1, n-1 ) || substr( l_stmt, m+2 );
 end loop;
 loop
  n := nvl( instr( l_stmt, '--' ), 0 );
  exit when (n=0);
  m := nvl( instr( l_stmt, chr(10), n+1 ), length(l_stmt) );
  if ( m = 0 ) then m := length(l_stmt); end if;
  l_stmt := substr( l_stmt, 1, n-1 ) || substr( l_stmt, m+1 );
 end loop;
 loop
 n := nvl( instr( l_stmt, '''' ), 0 );
 exit when (n=0);
 o := n;
 loop
  m := nvl( instr( l_stmt, '''', o+1 ), length(l_stmt) );
  if ( m = 0 ) then m := length(l_stmt); end if;
  if ( substr(l_stmt,m+1,1)  = '''' )
  then
   o := m+1;
  else
   exit;
  end if;
 end loop;
 l_stmt := substr( l_stmt, 1, n-1 ) || substr( l_stmt, m+1 );
 end loop;
    loop
  n := nvl( instr( l_stmt, ':' ), 0 );
  exit when ( n= 0 );
        l_str := upper(substr(l_stmt,n,31));
        for j in 2 .. length(l_str)
        loop
           l_ch := substr( l_str, j, 1 );
     if ( l_ch not between 'A' and 'Z' and
    l_ch not between '0' and '9' and
    l_ch <> '_' )
     then
    l_str := substr(l_str,1,j-1);
    exit;
     end if;
        end loop;
        if ( length(l_str) > 1 )
  then
   if (instr( l_added, l_str||':' ) = 0)
   then
      l_added := l_str || l_added;
      l_binds(l_binds.count+1) := l_str;
   end if;
        end if;
  l_stmt := substr( l_stmt, n+1 );
    end loop;
 return l_binds;
end get_binds;

Awesome ...

BC, November 25, 2008 - 11:47 am UTC

Tom,

This is fantastic, thank you so much.

BC

Another Dynamic puzzle

reader, January 16, 2009 - 6:31 pm UTC

Tom,

I have following proc that passed two parameter however during execution it returns "ORA-00933: SQL command not properly ended" pointing at line of cursor execution. Is there a way to see how string looks at the time or just before it errors out? The SQL injection is not a concern in this case that's the reason for this approach.
Could you help with this?

Grateful

declare

p_date_format varchar2(10);
p_where varchar2(4000);

l_query varchar2(4000);

TYPE my_ref_cur IS REF CURSOR;
cv_x my_ref_cur;

l_a t.a%type;
l_d t.d%type;
l_count integer;

begin

p_date_format := 'mm';
p_where := ' WHERE c = ''Y'' AND f is not null AND h = ''something'' ';

l_query := 'SELECT a, trunc (d, :date_format), count(*) FROM t :where GROUP BY a, TRUNC (d, :date_format) ';

OPEN cv_x FOR l_query USING p_date_format, p_where, p_date_format;
loop
FETCH cv_x INTO l_a, l_d, l_count;
EXIT WHEN cv_x%NOTFOUND;

dbms_output.put_line( 'name'||l_a );
dbms_output.put_line( 'date'||l_d );
dbms_output.put_line( 'count'||l_count );

end loop;
CLOSE cv_x;

END;
Tom Kyte
January 16, 2009 - 8:31 pm UTC

well, sure, you already know how...

dbms_output.put_line()

however, you are trying to "bind in" SQL - you bind in inputs to SQL. You do not bind in SQL itself.

eg: using binds is a 100% assured way to avoid sql injection since it is impossible to CHANGE THE SQL if you bind.

You want to change the sql, therefore you by definition cannot bind. You would concatenate - not bind - p_where.


Dynamic declaration of a variable

DM, May 06, 2009 - 3:35 am UTC

Hi tom

How we can dynamically declare a variable?

thanks
DM_IND
Tom Kyte
May 11, 2009 - 9:40 am UTC

you would truly need to define what "dynamically declare a variable" means.

In a programming language, variables are typically defined *at compile time*.

If you need to stash away a set of things you do not know until runtime, you might be interested in a plsql table indexed by varchar2...


ops$tkyte%ORA9IR2> create or replace procedure p
  2  as
  3          type stash is table of varchar2(4000) index by varchar2(30);
  4
  5          l_data stash;
  6          l_index varchar2(30);
  7  begin
  8          l_data( 'my first variable' ) := 'hello';
  9          l_data( 'my second variable' ) := 'world';
 10
 11
 12          dbms_output.put_line( l_data( 'my first variable' ) );
 13          dbms_output.put_line( l_data( 'my second variable' ) );
 14
 15          l_index := l_data.first;
 16          while (l_index is not null)
 17          loop
 18                  dbms_output.put_line( l_index || ' = ' || l_data(l_index) );
 19                  l_index := l_data.next(l_index);
 20          end loop;
 21  end;
 22  /

Procedure created.

ops$tkyte%ORA9IR2> exec p
hello
world
my first variable = hello
my second variable = world

PL/SQL procedure successfully completed.

How to use Bind variables in this function

Sita, May 12, 2009 - 11:56 pm UTC

Tom

I developed this function to retrieve the partitions matching the input dates. If count(*) > 0 then i display the partition.
I want to improve this by using Bind variables.
Could you advise how to place Bind Variables.

Code is :


create or replace function ret_part_match(p_table in VARCHAR2,p_owner in VARCHAR2 DEFAULT USER)
return varchar2
is
l_partition varchar2(30) ;
l_cnt number := 0;
l_rowtotal number := 0;
begin
for i in (select 'select count(*), '''||partition_name||''' partition_name from '||table_name||' partition('||partition_name||')'||' WHERE rpt_dt in ( ''200807'', ''200808'', ''200809'', ''200810'', ''200811'', ''200812'') ' QUERY from user_tab_partitions where table_name=p_table)
loop
null;
--dbms_output.put_line('command: '||i.query) ;
execute immediate i.query into l_cnt, l_partition ;
if ( l_cnt > 0 )
then
dbms_output.put_line('Partition_name: '||l_partition||', '||' Count :'||l_cnt) ;
l_rowtotal := l_rowtotal + l_cnt ;
else
null;
end if;

end loop;
dbms_output.put_line(CHR(10)||'Total Row count: '||l_rowtotal) ;
return 0;
end;
/




Tkprof shows below:
********************************************************************************

select count(*), 'NM_A200705' partition_name
from
NON_MACHINE_TAB partition(NM_A200705) WHERE rpt_dt in ( '200807', '200808',
'200809', '200810', '200811', '200812')


call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.01 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1 0.00 0.00 0 3 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 0.01 0.01 0 3 0
Tom Kyte
May 13, 2009 - 1:41 pm UTC

no procedural code needs be invented for this.

ops$tkyte%ORA10GR2> /*
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> CREATE TABLE t
ops$tkyte%ORA10GR2> (
ops$tkyte%ORA10GR2>   dt  date,
ops$tkyte%ORA10GR2>   x   int,
ops$tkyte%ORA10GR2>   y   varchar2(30)
ops$tkyte%ORA10GR2> )
ops$tkyte%ORA10GR2> PARTITION BY RANGE (dt)
ops$tkyte%ORA10GR2> (
ops$tkyte%ORA10GR2>   PARTITION part1 VALUES LESS THAN (to_date('01-jan-2008','dd-mon-yyyy')) ,
ops$tkyte%ORA10GR2>   PARTITION part2 VALUES LESS THAN (to_date('01-jan-2009','dd-mon-yyyy'))
ops$tkyte%ORA10GR2> )
ops$tkyte%ORA10GR2> /
ops$tkyte%ORA10GR2>
ops$tkyte%ORA10GR2> insert into t (dt,x,y)
ops$tkyte%ORA10GR2> select to_date('01-jan-2007')+mod(rownum,365*2), rownum, rownum
ops$tkyte%ORA10GR2>   from all_objects
ops$tkyte%ORA10GR2> /
ops$tkyte%ORA10GR2> */
ops$tkyte%ORA10GR2>

ops$tkyte%ORA10GR2> select uo.subobject_name, count(*)
  2    from t, user_objects uo
  3   where uo.data_object_id = dbms_rowid.rowid_object(t.rowid)
  4   group by uo.subobject_name
  5  /

SUBOBJECT_NAME                   COUNT(*)
------------------------------ ----------
PART1                               24918
PART2                               24820



just add a where clause.

Retrieve partition matching the input data

Sita, May 13, 2009 - 3:27 pm UTC

Tom
I am on 10.2.0.3
I tried to run the query you but taking long. I cancelled it after 7 minutes. 

What might be the cause ?

 1  select uo.subobject_name, count(*)
  2        from NON_MACHINE_TAB t, user_objects uo
  3       where uo.data_object_id = dbms_rowid.rowid_object(t.rowid)
  4*      group by uo.subobject_name
14:52:56 SQL> /

^C     where uo.data_object_id = dbms_rowid.rowid_object(t.rowid)
                               *
ERROR at line 3:
ORA-01013: user requested cancel of current operation


Elapsed: 00:07:42.35

15:04:54 SQL> show parameter optimizer

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
optimizer_dynamic_sampling           integer     2
optimizer_features_enable            string      10.2.0.3
optimizer_index_caching              integer     0
optimizer_index_cost_adj             integer     100
optimizer_mode                       string      ALL_ROWS
optimizer_secure_view_merging        boolean     TRUE



Tom Kyte
May 13, 2009 - 3:42 pm UTC

how long does count(*) against just the table itself take?

with uo
as
(select subobject_name, data_object_id 
   from user_objects
  where table_name = 'NON_MACHINE_TAB')
select uo.subobject_name, count(*)
  from NON_MACHINE_TAB t, uo
 where uo.data_object_id = dbms_rowid.rowid_object(t.rowid)
 group by uo.subobject_name


is another way to phrase that.


continuation of above

Sita, May 13, 2009 - 3:49 pm UTC

tkprof is attached below for the same query.
note that query result did not come back even after 5 minutes and cancelled.
********************************************************************************

select uo.subobject_name, count(*)
from NON_MACHINE_TAB t, user_objects uo
where uo.data_object_id = dbms_rowid.rowid_object(t.rowid)
group by uo.subobject_name

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.09 0.24 0 8 0 0
Execute 1 0.01 0.00 0 0 0 0
Fetch 1 353.40 359.05 0 1053 0 0
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 3 353.50 359.30 0 1061 0 0

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 65

Rows Row Source Operation
------- ---------------------------------------------------
0 HASH GROUP BY (cr=0 pr=0 pw=0 time=2767 us)
13444999 HASH JOIN (cr=1053 pr=0 pw=0 time=349682591 us)
6007 VIEW USER_OBJECTS (cr=611 pr=0 pw=0 time=73430 us)
6007 UNION-ALL (cr=611 pr=0 pw=0 time=67413 us)
6007 FILTER (cr=610 pr=0 pw=0 time=61384 us)
6012 TABLE ACCESS FULL OBJ$ (cr=472 pr=0 pw=0 time=97458 us)
67 TABLE ACCESS BY INDEX ROWID IND$ (cr=138 pr=0 pw=0 time=2669 us)
68 INDEX UNIQUE SCAN I_IND1 (cr=70 pr=0 pw=0 time=1111 us)(object id 39)
0 INDEX RANGE SCAN I_LINK1 (cr=1 pr=0 pw=0 time=96 us)(object id 107)
13445000 PARTITION RANGE ALL PARTITION: 1 64 (cr=442 pr=0 pw=0 time=13445547 us)
13445000 BITMAP CONVERSION TO ROWIDS (cr=442 pr=0 pw=0 time=12876 us)
474 BITMAP INDEX FAST FULL SCAN I3_NON_MACHINE_TAB PARTITION: 1 64 (cr=442 pr=0 pw=0 time=33377 us)(object id 15746
85)


Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 1 0.00 0.00
SQL*Net break/reset to client 1 0.00 0.00
********************************************************************************

Tom Kyte
May 13, 2009 - 4:24 pm UTC

tkprof for count(*) please

wonder why it is going to the table, it doesn't need to...

Sita, May 13, 2009 - 3:51 pm UTC

count(*) takes 10 ms.

SQL> select count(*) from NON_MACHINE_TAB ;

  COUNT(*)
----------
  20582265

Elapsed: 00:00:00.10

Tom Kyte
May 13, 2009 - 4:25 pm UTC

can you do select count(rowid) for me instead of just count(*)

count(rowid) against table and tkprof

sita, May 13, 2009 - 5:11 pm UTC

SQL> select count(rowid) from NON_MACHINE_TAB ;

COUNT(ROWID)
------------
    20582265

Elapsed: 00:00:10.31
SQL> select count(*) from NON_MACHINE_TAB ;

  COUNT(*)
----------
  20582265

Elapsed: 00:00:00.10



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

select count(rowid)
from
 NON_MACHINE_TAB


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.00       0.01          0          0          0           0
Fetch        2     10.03      10.01          0        604          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4     10.04      10.02          0        604          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 65

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=604 pr=0 pw=0 time=10011222 us)
20582265   PARTITION RANGE ALL PARTITION: 1 64 (cr=604 pr=0 pw=0 time=404 us)
20582265    BITMAP CONVERSION TO ROWIDS (cr=604 pr=0 pw=0 time=8686 us)
    714     BITMAP INDEX FAST FULL SCAN I3_NON_MACHINE_TAB PARTITION: 1 64 (cr=604 pr=0 pw=0 time=20724 us)(object id 157468
5)

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

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

select count(*)
from
 NON_MACHINE_TAB


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.01       0.00          0          0          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        2      0.07       0.06          0        604          0           1
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        4      0.08       0.07          0        604          0           1

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 65

Rows     Row Source Operation
-------  ---------------------------------------------------
      1  SORT AGGREGATE (cr=604 pr=0 pw=0 time=67552 us)
    714   PARTITION RANGE ALL PARTITION: 1 64 (cr=604 pr=0 pw=0 time=60193 us)
    714    BITMAP CONVERSION COUNT (cr=604 pr=0 pw=0 time=64727 us)
    714     BITMAP INDEX FAST FULL SCAN I3_NON_MACHINE_TAB PARTITION: 1 64 (cr=604 pr=0 pw=0 time=12884 us)(object id 157468
5)




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

Tom Kyte
May 13, 2009 - 5:17 pm UTC

it has to be the call to dbms_rowid then


if you count( dbms_rowid.rowid_object(t.rowid) ) - does the runtime explode?

Dynamic Sql and union

Sal, May 15, 2009 - 11:15 am UTC

Tom,

I am writing a dynamic SQL query where we can union any combination of 5 tables (t1-t5) based on the parameters passed into the stored procedure. Suppose you passed in parameters to the stored procedure telling it to search on t1,t4 and t5, the query look as below:


select * from t1 where upper(t1.name) like :str
union
select * from t4 where upper(t4.name) like :str or upper(t4.col2) like :str
union
select * from t5 where upper(t5.col3) like :str


str is of the form 'TOM%'

Different tables have a different number of fields to be searching on. However, the string that is searched on is always the same for all tables.

As you can see, due to many different possibilities, when I execute this dynamic SQL, I have to account for 1-n number of binds.

One way I thought of resolving this was to use the with clause:
With data as (select :str as keyword from dual) 

select * from t1 where upper(t1.name) like (select keyword from data)
union
select * from t4 where upper(t4.name) like (select keyword from data) or upper(t4.col2) like (select keyword from data)
union
select * from t5 where upper(t5.col3) like (select keyword from data)



Is that a feasible approach or am I adding extra overhead with the dual access that can be avoided? Any insights will be much appreciated.

Thanks!
Tom Kyte
May 15, 2009 - 2:07 pm UTC

that'll work nicely

the query itself, slow, but pinging dual like that with the scalar subquery will be fine.


make sure you know the difference between union and union all - and make sure you wanted to use union. Your query performance is going to be bad enough as it is...

Query performance

Sal, May 15, 2009 - 4:29 pm UTC

Tom,

Will the query performance still be bad if I am using FBIs ( eg upper(col1) etc.)? If so, can you tell why it would be bad?

Thanks!
Tom Kyte
May 23, 2009 - 10:41 am UTC

Say you have a 1,000,000 row table

Say column C in this table is UNIQUE.

Suppose you query:

select * from t where upper(c) = ?


do you think that

a) a function based index would be the best thing to help that perform really fast

b) a function based index would negatively impact performance


How about you tell us why you think a function based index would be bad???? I don't understand the logic behind this thinking - so I'm at a loss as to how to address your concerns.

More info

Sal, May 21, 2009 - 10:07 am UTC

Tom,

I think I left out a crucial part of the query w.r.t performance and that is probably why you said the above query will be slow.

The query looks more like this:

select * from (select * from t1 where upper(t1.name) like :str order by name) where rownum <= 10

union
select * from (select * from t4 where upper(t4.name) like :str or upper(t4.col2) like :str order by name) where rownum <= 10

union
select * from (
select * from t5 where upper(t5.col3) like :str order by name) where rownum <= 10





This will cause the materialization of the sub-sets and the union will only be operating on 30 rows. If I did not have the rownum, then I can see how this would be horribly bad (since union causes sorting). Is that what you were also thinking?

Also, thanks for pointing out about the union all. We should be using union all - the error was not noticed because there are other columns in the select that cause all records to be unique across the unions.
Tom Kyte
May 23, 2009 - 1:17 pm UTC

you might want to order by upper(t1.name) etc - unless you want to

a) find all of them in t1
b) sort them by name
c) return the first 10

it would be better if you can order by the indexed column if possible.

dynamic calculation

david, June 22, 2009 - 8:11 am UTC

Hi Tom

I have a small program which reads from a table to obtain some calculation formulas (there are around 8000 formulas).

The code is as follows:

declare
l_formula varchar2(10);
l_results number;
begin
for i in 1..10
loop
l_formula := '1 + '||i;
dbms_output.put_line(l_formula);
execute immediate 'begin :exit := ' || l_formula || '; end;';
end loop;
end;
/


This code does not work, I think I need to use dbms_sql but the output should be something like

begin :exit := 1 + 1; end;
begin :exit := 1 + 2; end;

etc

I always end up with tons of sql with literals, how can we avoid this (it is causing us ORA-04031 errors)

Thanks!
Tom Kyte
June 22, 2009 - 3:34 pm UTC

You can and will find a different approach to this, a method to get them compiled and stored in the database.


why do something over and over and over (dynamically interpret 1+2) when you can compile it once and refer to it over and over and over again....


create a view that selects out lots of "formulas" - make the thing that maintains your table generate code that is compiled once so you can statically reference it.

else- 4031 will be something you are intimately familiar with.

dynamic calculation

David, June 23, 2009 - 1:38 am UTC

Hi Tom

That is exactly my problem, ORA-04031 (but it happens once a month pretty exact and I have to shutdown)

The example I have posted is pretty simple.

The real thing is the table (let's call it formula) contains formulas such as:

1+2+756+7+23+32
...............
...............

8000 of these and the formula are generated on fly

select ..
into l_formula
from formula
where formula_id = x;

final_formula = some_variable + some_more_variable || ' + ' || l_formula

End up having like 15000 SQL with literals everyday

Your suggestion is store formulas as compiled code?

Thank you
Tom Kyte
June 26, 2009 - 8:56 am UTC

statically compile them. yes.


you have something that populates these formulas, have that be a code generator instead, that way - AT COMPILE TIME - you know the forumlas are valid and will execute at runtime AND at runtime - they will run fast and will not flood the shared pool...

store as compiled core

Peter, June 28, 2009 - 2:55 am UTC

Hello Tom

I have look the last couple of posts (I have similar problem as David)

How do you store those formula as compile code in a table?

If you store a:=1+1 instead of 1+1 you still need to execute them dynamically no? Query the table and store the formula in a pl/sql variable and do execute immediate

Thank you
Tom Kyte
July 06, 2009 - 5:26 pm UTC

re-read my answers please.

I'm telling you "do not do that, it would be a very bad idea, it will not perform, it is not a smart thing to do"

you would generate code, you would not save formulas in a table and dynamically try to interpret them.

OK

Kumar, November 23, 2009 - 9:13 am UTC

Hi,
I have some problem in below statement. I am trying to generate the dynamic query but getting errors.
Can you please help me out?

select 'select user, (select count(*) from dba_objects where owner = '||uname||'cnt from
(select username uname from dba_users where username = ''SCOTT'')'
from dual

I need the SCOTT value to be passed in the inner scalar subquery

Thanks
Tom Kyte
November 23, 2009 - 4:40 pm UTC

you give me no context here.

What environment, language, thing - whatever - would be doing this passing in.

OK

Kumar, November 23, 2009 - 9:43 pm UTC

Hi,
I am using this in a PL/SQL procedure
Tom Kyte
November 24, 2009 - 10:23 am UTC

so, give me a tad bit more code to look at, not sure why you would be doing what you are doing.

Don't know if you are trying to get the results of this query (in which case - do not select from dual, just ASSIGN A STRING TO A STRING) or what.


Inconsistent column length from Select column metadata

Igor, January 06, 2010 - 8:33 am UTC

Hi, 

It seems that getting SELECT result set column metadata is not reliable for cursor_sharing=force (got this result on different versions, 10.2.0.x and 11.2.0.1). 
When got correct result using cursor_sharing=EXACT, switching to session with cursor_sharing=FORCE get also correct (cached) metadata.
This was also observed when using OCI code (so, it's not dbms_sql specific)

Example:

SQL> alter system flush shared_pool
  2  /

System altered.

SQL> alter system flush shared_pool
  2  /

System altered.

SQL> alter system flush shared_pool
  2  /

System altered.

SQL> conn mit_mig@potiro10
Enter password:
Connected.

SQL> alter session set cursor_sharing=force;

Session altered.

SQL> set echo on
SQL> @collens2.sql
SQL> set serveroutput on size unlimited
SQL>
SQL> declare
  2        l_cur   int default dbms_sql.open_cursor;
  3        l_desc  dbms_sql.desc_tab;
  4        l_ncols int;
  5      begin
  6        dbms_sql.parse(l_cur,  'select ''"''||''C1''||''"'' as a from dual'
  7  ,
  8  dbms_sql.native);
  9        dbms_sql.describe_columns(l_cur, l_ncols, l_desc);
 10        dbms_sql.close_cursor(l_cur);
 11        for i in 1..l_ncols loop
 12         dbms_output.put_line(l_desc(i).col_max_len);
 13       end loop;
 14     end;
 15  /
96

PL/SQL procedure successfully completed.

SQL> alter session set cursor_sharing=exact;

Session altered.

SQL> @collens2.sql
SQL> set serveroutput on size unlimited
SQL>
SQL> declare
  2        l_cur   int default dbms_sql.open_cursor;
  3        l_desc  dbms_sql.desc_tab;
  4        l_ncols int;
  5      begin
  6        dbms_sql.parse(l_cur,  'select ''"''||''C1''||''"'' as a from dual'
  7  ,
  8  dbms_sql.native);
  9        dbms_sql.describe_columns(l_cur, l_ncols, l_desc);
 10        dbms_sql.close_cursor(l_cur);
 11        for i in 1..l_ncols loop
 12         dbms_output.put_line(l_desc(i).col_max_len);
 13       end loop;
 14     end;
 15  /
4

PL/SQL procedure successfully completed.

SQL> alter session set cursor_sharing=force;

Session altered.

SQL> @collens2.sql
SQL> set serveroutput on size unlimited
SQL>
SQL> declare
  2        l_cur   int default dbms_sql.open_cursor;
  3        l_desc  dbms_sql.desc_tab;
  4        l_ncols int;
  5      begin
  6        dbms_sql.parse(l_cur,  'select ''"''||''C1''||''"'' as a from dual'
  7  ,
  8  dbms_sql.native);
  9        dbms_sql.describe_columns(l_cur, l_ncols, l_desc);
 10        dbms_sql.close_cursor(l_cur);
 11        for i in 1..l_ncols loop
 12         dbms_output.put_line(l_desc(i).col_max_len);
 13       end loop;
 14     end;
 15  /
4

PL/SQL procedure successfully completed.

SQL> conn sys@potiro10 as sysdba
Enter password:
Connected.
SQL> alter system flush shared_pool
  2  /

System altered.

SQL> alter system flush shared_pool
  2  /

System altered.

SQL> alter system flush shared_pool
  2  /

System altered.

SQL> conn mit_mig@potiro10
Enter password:
Connected.
SQL> conn mit_mig@potiro10

SQL> alter session set cursor_sharing=force;

Session altered.

SQL> set echo on
SQL> @collens2.sql
SQL> set serveroutput on size unlimited
SQL>
SQL> declare
  2        l_cur   int default dbms_sql.open_cursor;
  3        l_desc  dbms_sql.desc_tab;
  4        l_ncols int;
  5      begin
  6        dbms_sql.parse(l_cur,  'select ''"''||''C1''||''"'' as a from dual'
  7  ,
  8  dbms_sql.native);
  9        dbms_sql.describe_columns(l_cur, l_ncols, l_desc);
 10        dbms_sql.close_cursor(l_cur);
 11        for i in 1..l_ncols loop
 12         dbms_output.put_line(l_desc(i).col_max_len);
 13       end loop;
 14     end;
 15  /
96

PL/SQL procedure successfully completed.

Comments are welcome.

Thank you and regards


Tom Kyte
January 06, 2010 - 10:11 am UTC

nothing wrong there - it is a known and "to be expected" side effect of cursor sharing force/similar. One of the reasons I don't like either of them. The fact you would need to use force/similar tells me with 100% degree of certainty - there is a bug in the developed code.

with cursor sharing = force, see what the query becomes:

ops$tkyte%ORA10GR2> alter session set cursor_sharing=force;

Session altered.


ops$tkyte%ORA10GR2> select '"'||'C1'||'"' as a from dual d1;

A
----
"C1"

ops$tkyte%ORA10GR2> alter session set cursor_sharing=force;

Session altered.

ops$tkyte%ORA10GR2> select '"'||'C1'||'"' as a from dual d2;

A
-------------------------------------------------------------------------------
"C1"

ops$tkyte%ORA10GR2> select sql_text from v$sql where sql_text like 'select % as a from dual d_';

SQL_TEXT
-------------------------------------------------------------------------------
select '"'||'C1'||'"' as a from dual d1
select :"SYS_B_0"||:"SYS_B_1"||:"SYS_B_2" as a from dual d2



Now, you tell me - how big is :"SYS_B_0"||:"SYS_B_1"||:"SYS_B_2" ???

we don't know, so we put a value in there - we use 32 (*hence 96*) as a starter and hope for the best.

If you stay with 32 bytes or less:

ops$tkyte%ORA10GR2> select '"'||'C123456789012345678901234567890X'||'"' as a from dual d2;

A
-------------------------------------------------------------------------------
"C123456789012345678901234567890X"

ops$tkyte%ORA10GR2> select sql_text from v$sql where sql_text like 'select % as a from dual d_';

SQL_TEXT
-------------------------------------------------------------------------------
select '"'||'C1'||'"' as a from dual d1
select :"SYS_B_0"||:"SYS_B_1"||:"SYS_B_2" as a from dual d2




cursor reuse, but go over that and....


If you do things with 32 or less, we can reuse - but if you go over that....


ops$tkyte%ORA10GR2> select '"'||'C12345678901234567890123456789012345'||'"' as a from dual d2;

A
-------------------------------------------------------------------------------
"C12345678901234567890123456789012345"

ops$tkyte%ORA10GR2> select sql_text from v$sql where sql_text like 'select % as a from dual d_';

SQL_TEXT
-------------------------------------------------------------------------------
select '"'||'C1'||'"' as a from dual d1
select :"SYS_B_0"||:"SYS_B_1"||:"SYS_B_2" as a from dual d2
select :"SYS_B_0"||:"SYS_B_1"||:"SYS_B_2" as a from dual d2


bam, new cursor - we hit a bind mismatch.


This is an obvious side effect of overbinding - cursor sharing = force/similar removes a TON of information.

Thank you

Igor, January 06, 2010 - 12:20 pm UTC

>> The fact you would need to use force/similar tells me with 100% degree of certainty - there is a bug in the developed code.

I don't disagree. If only problem would include just technical "component", it would be much easier to resolve...
Tom Kyte
January 06, 2010 - 1:29 pm UTC

Well, if it helps you any, explain to the coders manager (the one that isn't technical) that they coders have a huge SECURITY RISK coded in your application, one that has cost businesses billions of dollars and continues to do so - to see the results of what they are doing, just google search for sql injection, it happens over and over and over again.

and cursor sharing has this much impact on sql injection issues:


0.0000%


Igor, January 07, 2010 - 3:18 am UTC

Hi,

Yes, I am aware of that.
Still, it's not easy (there are tons of legacy parts, multi layered application, long long time running in productions etc). It seems rather to be "process" to convince...

On tehnical part, one thing I don't get, why when running this PL/SQL block with cursor_sharing=FORCE *after* same block with cursor_sharing=EXACT ==> it *reuses* information from "exact" ? (It's a different cursor, why it would change result)

Thank you and regards
Tom Kyte
January 11, 2010 - 8:52 am UTC

it found the literal SQL in the shared pool on the soft parse part of the routine, there was no need to go farther - no need to actually hard parse it - until you change it.

Normally, with cursor sharing = force, this would not be even noticed - the premise with you having to use force in the first place is that you are hard parsing like mad.

Igor, January 12, 2010 - 7:27 am UTC

Hi,

Not sure I understood your comment.
Normally, when it's cursor_sharing=FORCE, SQL text is not the same as with cursor_sharing=EXACT (Oracle parses text to change constants, before looking at the shared pool ?). So, it should be different SQL, different cursor... ?
In above example, we see that result (value got from metadata description), depends if there was already run query using cursor_sharing=EXACT ?


Thank you and regards,
Igor

what is the meaning of 'immediate' in 'execute immediate' ?

Ranjith, February 25, 2010 - 7:48 pm UTC

Are there any other keywords available for 'execute' command in PL/SQL other than 'immediate'?
Tom Kyte
March 01, 2010 - 11:15 am UTC

it is borrowed from pro*c and other embedded languages, it is just the decided upon syntax by some committee somewhere.


execute immediate is it.

Execute immediate

satin satin, May 20, 2010 - 2:49 pm UTC

I having little issue with execute immediate when i try this tthe procedure it is not working and while the same is working from pl/sql block .Can you please tell me why and what is missing 

SQL> create or replace procedure crt_table_prc as
  2   l_sql_stmnt varchar2(2000);
  3  begin
  4     l_sql_stmnt := 'create table cust_rec (cust_num number, cust_name varchar2(20))';
  5     begin
  6        execute immediate l_sql_stmnt;
  7     end;
  8   exception
  9   when others then
 10    dbms_output.put_line(sqlerrm);
 11  end;
 12  /

Procedure created.

SQL> declare
  2  begin
  3    begin
  4         crt_table_prc;
  5    end;
  6   exception
  7    when others then
  8       dbms_output.put_line(sqlerrm);
  9  end;
 10  /
ORA-01031: insufficient privileges

PL/SQL procedure successfully completed.

SQL> declare
  2    l_sql_stmnt varchar2(2000);
  3  begin
  4      l_sql_stmnt := 'create table cust_rec (cust_num number, cust_name varchar2(20))';
  5   begin
  6      execute immediate l_sql_stmnt;
  7   end;
  8     dbms_output.put_line('table created ');
  9   exception
 10    when others then
 11       dbms_output.put_line(sqlerrm);
 12  end;
 13  /
table created

PL/SQL procedure successfully completed.



when

Tom Kyte
May 24, 2010 - 12:14 pm UTC

http://asktom.oracle.com/pls/asktom/ASKTOM.download_file?p_file=6551289900368934430

you need create table granted to you directly, not via a role

or you need to use an invokers rights routine, which would make your sql in your plsql execute as if it were in an anonymous block of code.

cursor as in argument

Lasse Jenssen, May 23, 2010 - 7:12 am UTC

I have written an anonymous block to delete rows from different tables. Here i have a test case (guess you have emp and dept table available), and a question:
<pre>
create table t_emp as select * from scott.emp;
create table t_dept as select * from scott.dept;

set serveroutput on
declare
type rowidArray is table of rowid index by binary_integer;
type myRec is record (
id rowidArray
);
type myCur is ref cursor;

sql_emp varchar2(1000):= 'SELECT rowid FROM t_emp where deptno<30';
sql_dept varchar2(1000):='SELECT rowid FROM t_dept WHERE deptno<30';

procedure slett(tname in varchar2, mysql in varchar2) is
l_record myRec;
l_array_size number default 500;
l_done boolean;
l_cnt number default 1;
l_cnt_del number default 1;
c myCur;
sql_del varchar2(1000);
begin
open c for mysql;
loop
dbms_application_info.set_client_info( 'Table: '||tname||', Processing ' || l_cnt || ' thru ' || (l_cnt+l_array_size-1) );

fetch c bulk collect into l_record.id LIMIT l_array_size;
l_done := c%notfound;

sql_del:='delete from ' || tname ||' where rowid= :1';

forall i in 1 .. l_record.id.count
execute immediate sql_del using l_record.id(i);

l_cnt_del:= l_cnt_del + l_record.id.count;

commit;

exit when (l_done);
l_cnt := l_cnt + l_array_size;
end loop;
dbms_output.put_line('Table: '||tname||', Rows deleted: ' || l_cnt_del);
end slett;
begin
slett('T_EMP',sql_emp);
slett('T_DEPT',sql_dept);
end;
/
</pre>

Question: Is there a way to pass a cursor into the procedure instead of the SQL text?
Tom Kyte
May 24, 2010 - 1:05 pm UTC

ops$tkyte%ORA11GR2> declare
  2    l_cursor sys_refcursor;
  3
  4    procedure slett(tname in varchar2, c in sys_refcursor ) is
  5      type array is table of rowid;
  6      l_data       array;
  7      l_array_size number default 500;
  8      l_cnt        number default 1;
  9      l_cnt_del    number default 1;
 10      sql_del varchar2(1000);
 11    begin
 12      sql_del:='delete from ' || dbms_assert.SQL_OBJECT_NAME(tname) ||' where rowid= :1';
 13
 14      loop
 15        dbms_application_info.set_client_info
 16        ( 'Table: '||tname||', Processing ' || l_cnt || ' thru ' || (l_cnt+l_array_size-1) );
 17
 18        fetch c bulk collect into l_data LIMIT l_array_size;
 19
 20        forall i in 1 .. l_data.count
 21          execute immediate sql_del using l_data(i);
 22
 23        l_cnt_del:= l_cnt_del + l_data.count;
 24        commit;
 25
 26        exit when (c%notfound);
 27        l_cnt := l_cnt + l_array_size;
 28      end loop;
 29      close c;
 30      dbms_output.put_line('Table: '||tname||', Rows deleted: ' || l_cnt_del);
 31    end slett;
 32
 33  begin
 34      open l_cursor for select rowid from t_emp where deptno < 30;
 35      slett('T_EMP',l_cursor );
 36      open l_cursor for select rowid from t_dept where deptno < 30;
 37      slett('T_DEPT',l_cursor );
 38  end;
 39  /
Table: T_EMP, Rows deleted: 9
Table: T_DEPT, Rows deleted: 3

PL/SQL procedure successfully completed.

What is invokers Right Routine

satin satin, May 24, 2010 - 2:59 pm UTC

Sorry I didnot understand the answer .What is invookers right routine ? Why DDL is working well thru the pl/sql block and not via procedure.

Could you please elborate ?

Thanks
Tom Kyte
May 24, 2010 - 3:14 pm UTC

did you read the link? That tells you why you are getting the ora-1013. (clearly)

If you do not know what an invokers rights routine is - it will not be safe to recommend using it (you'd need to understand what implications that has). It would be best to check out the documentation and read a bit about it before using it.
http://docs.oracle.com/docs/cd/E11882_01/appdev.112/e10472/subprograms.htm#LNPLS00809
question for you - why are you trying to create a table in plsql, that is very very very odd. Unless you are writing an install script, it would be very very very very odd.

satin satin, May 24, 2010 - 3:01 pm UTC

I have missed the oracle version we are using 10g and migrating to 11g

Is PL/SQL compiler or interpreter ?

BT, August 02, 2010 - 4:42 am UTC

Hi Tom,

I’ve been your fan since long time.

One question was raised in our team during technical discussion.

Is PL/SQL compiler or interpreter?
One view was that PL/SQL compiles programs and generates P-code, therefore it should be compiler.

But there are some links on which PL/SQL is mentioned as an interpreter.

http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14251/adfns_dynamic_sql.htm
http://download.oracle.com/docs/cd/E16340_01/bi.1111/b32122/orbr_concepts2006.htm

Can you please give some details to understand the concepts?

Thank You
BT
Tom Kyte
August 02, 2010 - 9:05 am UTC

pl/sql is a language.

there is a pl/sql compiler that can compile pl/sql source code into p-code (byte code) to be interpreted OR into native code directly to be executed by the machine itself.




So, PL/SQL is a language

PL/SQL is a language which is compiled into p-code or native code by a compiler.


PL/SQL is not a compiler.
PL/SQL is not an interpreter.


PL/SQL is a language that by default can be compiled into p-code to be interpreted OR into native code optionally for direct machine execution.

ashish agrawal, October 17, 2010 - 8:42 am UTC

create table temp_tab
(
upld_usr_sid VARCHAR2(50),
btch_id NUMBER,
row_id NUMBER,
trd_ky_id NUMBER
);

create or replace procedure p1
as

BEGIN
TYPE err_info_tab IS TABLE OF temp_tab%ROWTYPE;
err_info_rec err_info_tab;

update stg_dma_bulk_upload
set ind_is_err = 3
where rowid in (select rid
from (select rowid rid,
row_number() over
(partition by TRD_KY_ID, TRD_DT, ACTVTY_DT, OLD_TRDNG_LGL_ENTTY_NM, OLD_TRDNG_ORGNZTNL_UNT_NM, TRDNG_LGL_ENTTY_NM, TRDNG_ORGNZTNL_UNT_NM order by rowid) rn
from stg_dma_bulk_upload
where btch_id = 106
)
where rn <> 1
)
returning upld_usr_sid,
btch_id,
row_id,
trd_ky_id,
bulk collect into err_info_rec;

COMMIT;

FORALL i IN err_info_rec.FIRST .. err_info_rec.LAST
insert into temp_tab values err_info_rec(i);

commit;
END p1;

This procedure is working fine.

ashish agrawal, October 17, 2010 - 8:49 am UTC

This is in continuation with my previous post.

below one when I use dynamic SQL, it gives below error.
ORA-00905: missing keyword

procedure giving error is as below:

parameter values are
p_batch_id := 106;
v_cols := 'TRD_KY_ID, TRD_DT, ACTVTY_DT, OLD_TRDNG_LGL_ENTTY_NM, OLD_TRDNG_ORGNZTNL_UNT_NM, TRDNG_LGL_ENTTY_NM, TRDNG_ORGNZTNL_UNT_NM';


create or replace procedure p2(p_batch_id number, v_cols varchar2)
is

BEGIN
TYPE err_info_tab IS TABLE OF temp_tab%ROWTYPE;
err_info_rec err_info_tab;

execute immediate
'update stg_dma_bulk_upload
set ind_is_err = 3
where rowid in (select rid
from (select rowid rid,
row_number() over
(partition by '||v_cols||' order by rowid) rn
from stg_dma_bulk_upload
where btch_id = '||p_batch_id||'
)
where rn <> 1
)
returning upld_usr_sid,
btch_id,
row_id,
trd_ky_id,
bulk collect into err_info_rec';

COMMIT;

FORALL i IN err_info_rec.FIRST .. err_info_rec.LAST
insert into temp_tab values err_info_rec(i);

commit;
END;
Tom Kyte
October 25, 2010 - 8:35 am UTC

bulk collect is NOT SQL, it is plsql. You are trying to mix dynamic sql and dynamically executed PLSQL.


You do not validate any inputs, you are highly subject to SQL Injection. Read about DBMS_ASSERT and use it.

You commit???? why do you COMMIT????? You know that first commit can only be described as a "bug that shows the developer didn't really know what a transaction is or does". But in my opinion - neither commit belongs there - I wish plsql didn't support commit and rollback most of the time.

I don't have a complete example here - so I'll not go any further, I cannot run your "working" code - let along attempt to debug your "not working" code.

There are limits to bulk collect with EXECUTE IMMEDIATE. returning, bulk collect into are NOT sql - they are an extension to sql provided by PLSQL.

You'd want to wrap the entire block as dynamic plsql - build a string:

create or replace procedure p2 ....
as
    /* variables would really be defined here in real life... Unlike your example */
begin

execute immediate 
'DECLARE  /* because we know that your code really doesn't have type after begin*/
  type ....
  err_info_rec ...
begin
   update ........ 
   forall i in .....
      insert ......
end;';

/* and no commits for you! Really bad practice to prematurely commit! */


ashish agrawal, October 18, 2010 - 12:37 am UTC

In continuation with previous post, it was compiled successfully. But was giving error during execution.
ORA-00905: MISSING KEYWORD
Tom Kyte
October 25, 2010 - 8:38 am UTC

because bulk collect, returning - are NOT sql.

Check Syntax without running.

Snehaish Das, March 22, 2011 - 2:10 am UTC


Hi Tom,

I am using execute immediate to form dynamic sqls and run them. The components of the query are got from the a table which the customer populates. So it is prone to syntactical errors. Is there anyway to check the syntactical errors without running the code.

Like

if v_sql syntax correct then
execute immediate v_sql;
else
log into error table;
end if;

Thanks and Regards,
Snehasish Das.
Tom Kyte
March 22, 2011 - 7:31 am UTC

You can parse the SQL, just beware that if you use dbms_sql to do that - parsing DDL actually EXECUTES it - so if you have anything beyond select/insert/update/delete/merge - be careful.

The generic, runtime totally data driven, my program can do anything model... A bad idea 99 times out of 100.

Urgent

Sam, June 22, 2011 - 9:08 am UTC

Dear Tom,
I have a procedure which is compiled successfully but while its execution its is giving err. In this i have used dynamic Update statement. Find the code snippet below.

s_sql :=
'UPDATE '
|| s_from_clause
|| '
SET GL.IS_BLOCKED =
|| ''Y''
|| ,
GL.UPDATED_DATE = SYSDATE
WHERE 1 = 1
'
|| s_where_clause
|| '
AND '
|| s_sn_select_clause
|| 'IN (SELECT COLUMN_VALUE
FROM TABLE(CAST(:I_SERIAL_NOS AS STR_ARRAY)))';

EXECUTE IMMEDIATE s_sql
USING i_serial_nos;


While execution its giving error. May be its due to some expression , but i am not able to fig it out. Kindly help me out.

Many thanks in Advance
Sam
Tom Kyte
June 22, 2011 - 10:48 am UTC

my car won't start. Here is a snippet of information:

it is a 2004 toyota prius.

I won't tell you anything else such as the information on the dashboard (sort of like error messages), the sound it makes or doesn't make (other supporting information) and so on.

Please - it is URGENT...


when you can answer me... I can answer you...



I see SQL INJECTION being a big part of your life - this is a really bad idea.

Hiii

Sam, June 22, 2011 - 2:13 pm UTC

Thanks for your quick reply.

Please find the more details below :

s_from_clause := 'Table_A G'
s_where_clause := ' AND L.CIRCLE_ID = ' || I_CIRCLE_ID || '
AND G.IS_ACTIVE=''Y'';

While executing it is throwing error that expression is missing.

Please let me know if you need any more information
Tom Kyte
June 22, 2011 - 2:17 pm UTC

I need a full up working example.

create table...
full stored procedure (JUST the broken parts - I don't need all of your code, just ENOUGH to demonstrate the issue)


suggestion:

add

dbms_output.put_line( s_sql );

BEFORE your execute and run it in sqlplus, look at the sql you are generating. I'm pretty sure (99.99%) you'll be able to fix this right up yourself once you look at the SQL you are generating - it should be very obvious what you are doing wrong...

Bind Variables PL SQL

A reader, June 23, 2011 - 2:59 am UTC

Hi Tom,

For the clear understanding I need your verification. In case of writing a PL SQL object, say package or procedure how does bind variables come into effect.

I have a procedure:

create or replace procedure updSal(p_empno in number)
as
  begin
    update emp
    set sal=sal*2
    where empno = p_empno;
    commit;
  end;
/
Question 1: Do I use the bind variables here? If I parameterize the query, PL SQL automatically understands it as bind variables

Now If I use the EXECUTE IMMEDIATE clause in the same procedure:

create or replace procedure updSal(p_empno in number)
as
  begin
    execute immediate
     'update emp set sal = sal*2 where empno = '||p_empno;
  commit;
  end;
/


Question 2: For me this is not case where bind variables are going to be used. Is it because of EXECUTE IMMEDIATE clause?

If I rewrite the EXECUTE IMMEDIATE clause in the following manner:

create or replace procedure updSal(p_empno in number)
as
  begin
    execute immediate
     'update emp set
     sal = sal*2 where empno = :x' using p_empno;
  commit;
  end;
/

Question 3: I think we are using bind variables with EXECUTE IMMEDIATE clause. Am I correct?

How can we achive bind variables effect in PL SQL objects?
Tom Kyte
June 23, 2011 - 8:04 am UTC

1) that is exactly like:

update emp
set sal=sal*2
where empno = :p_empno;


All references to plsql variables in a SQL statement are bind variables.


2) your use of execute immediate does not use binds - it is the wrong way to do it and subject to evil things like sql injection.

You should code it as:

execute immedaite 'udpate emp set sal = sal*2 where empno = :x' using p_empno;

Now it would use binds AND not be subject to sql injection.

3) that is correct






If you use static sql in plsql (your example 1), then you are using bind variables correctly AND you cannot be sql injected.

If you use dynamic sql in plsql (your examples 2 and 3), then YOU have to be careful to use bind variables correctly and you have to be really careful to avoid sql injection issues (proper use of bind variables alleviates that sql injection worry!)

nested table -list in a dynamic query

Jitendra, June 28, 2011 - 5:12 am UTC

Hi Tom,


Please suggest
how to handle the nested table list in dynamic query.

following is the sample code :




SET SERVEROUTPUT ON
set serveroutput on size 100000;

CREATE OR REPLACE TYPE STRING_ARRAY IS TABLE OF VARCHAR2(2000);


CREATE FUNCTION string_tokenizer(p_input_string IN VARCHAR2,
p_delim_char IN CHAR)
RETURN STRING_ARRAY
AS
v_ret_list STRING_ARRAY;
v_target BINARY_INTEGER;
v_index BINARY_INTEGER;
v_this_delim BINARY_INTEGER;
v_last_delim BINARY_INTEGER;
--Start local function


CREATE FUNCTION str_tokenizer(p_input_string IN VARCHAR2,
p_delim_char IN CHAR)
RETURN CIB_SPDF_STRING_ARRAY
AS
v_ret_list CIB_SPDF_STRING_ARRAY;
v_target BINARY_INTEGER;
v_index BINARY_INTEGER;
v_this_delim BINARY_INTEGER;
v_last_delim BINARY_INTEGER;
--Start local function
CREATE FUNCTION splitter_count(str in varchar2, delim in char) return int as
val int;
BEGIN
val := length(replace(str, delim, delim || ' '));
return val - length(str);
END;
--End local function
BEGIN
v_ret_list := CIB_SPDF_STRING_ARRAY();
v_index := 1;
v_last_delim := 0;
v_target := splitter_count(p_input_string, p_delim_char);
WHILE v_index <= v_target
LOOP
v_ret_list.extend();
v_this_delim := instr(p_input_string, p_delim_char, 1, v_index);
v_ret_list(v_index):= substr(p_input_string, v_last_delim + 1, v_this_delim - v_last_delim-1);
v_index := v_index + 1;
v_last_delim := v_this_delim;
END LOOP;
v_ret_list.extend();
v_ret_list(v_index):= substr(p_input_string, v_last_delim + 1);
RETURN v_ret_list;
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END string_tokenizer;


DROP TABLE UTIL_TBL CASCADE CONSTRAINTS;

CREATE TABLE UTIL_TBL
(
PRICE_LIST_ID NUMBER,
ATTRIBUTE_NAME VARCHAR2(50 BYTE),
ATTRIBUTE_VALUE VARCHAR2(4000 BYTE)
);

Insert into UTIL_TBL
(PRICE_LIST_ID, ATTRIBUTE_NAME, ATTRIBUTE_VALUE)
Values
(1220, 'GSP_LIST', 'C2P,C4P,C4S,CB4N,CB4T');
Insert into UTIL_TBL
(PRICE_LIST_ID, ATTRIBUTE_NAME, ATTRIBUTE_VALUE)
Values
(1220, 'PRODUCT_FAMILY', '204XRV,206XRV,211XRV,605-R-2E');


DROP TABLE dv_prod_gsp CASCADE CONSTRAINTS;

CREATE TABLE dv_prod_gsp
(
generic_service_item_id NUMBER,
generic_service_name VARCHAR2(50 BYTE),
generic_service_item_value VARCHAR2(4000 BYTE)
);

Insert into dv_prod_gsp
(generic_service_item_id, generic_service_name, generic_service_item_value)
Values
(1, 'C2P', 'BNN-12');
Insert into dv_prod_gsp
(generic_service_item_id, generic_service_name, generic_service_item_value)
Values
(2, ',C4P', 'BNN-56');
DROP TABLE dv_prod_fam CASCADE CONSTRAINTS;


CREATE TABLE dv_prod_fam
(
prodfam_id NUMBER,
generic_service_item_id NUMBER,
prodfam_name VARCHAR2(50 BYTE),
prodfam_value VARCHAR2(4000 BYTE)
);

Insert into dv_prod_fam
(prodfam_id,generic_service_item_id,prodfam_name, prodfam_value)
Values
(1,1, 'C2P-BNN-Fam', 'BNN-12');
Insert into dv_prod_fam
(prodfam_id,generic_service_item_id,prodfam_name, prodfam_value)
Values
(2,2,'C2P-BNN-Fam', 'BNN-56');




CREATE TABLE F_TEMP
(
generic_service_item_name VARCHAR2(50),
prodfam_name VARCHAR2(50)
);







DECLARE

cur_get_util SYS_REFCURSOR;
v_cur_get_util UTIL_TBL%ROWTYPE;
v_gsp_list STRING_ARRAY;
v_prod_fam_list STRING_ARRAY;

Sql_str Varchar2(4000) := 'INSERT INTO F_TEMP SELECT DISTINCT dpg.generic_service_name,
dpf.prodfam_name
FROM
dv_prod_gsp dpg,
dv_prod_fam dpf
WHERE dpg.generic_service_item_id = dpf.generic_service_item_id
AND dpg.generic_service_name in( SELECT * FROM TABLE(v_gsp_list))';

BEGIN

OPEN cur_get_util FOR
SELECT * FROM UTIL_TBL
WHERE price_list_id = 1220 ;

IF (cur_get_util IS NOT NULL)
THEN
LOOP
FETCH cur_get_util INTO v_cur_get_util;
EXIT WHEN cur_get_util%NOTFOUND;

IF v_cur_get_util.ATTRIBUTE_NAME = 'GSP_LIST'
THEN
v_gsp_list:= str_tokenizer(v_cur_get_util.ATTRIBUTE_VALUE,',');

ELSIF v_cur_get_util.ATTRIBUTE_NAME = 'PRODUCT_FAMILY'
THEN

IF v_cur_get_util.ATTRIBUTE_VALUE IS NOT NULL
THEN
v_prod_fam_list:=str_tokenizer(v_cur_get_util.ATTRIBUTE_VALUE,',');

Sql_str:= Sql_str||' AND dpf.prodfam_name in(SELECT * FROM TABLE(v_prod_fam_list))';

END IF;
END IF;
END LOOP;

CLOSE cur_get_util;


END IF;





execute immediate Sql_str;



commit;
END;




----------------------------
It gives the following error:

Error at line 5
ORA-00904: "V_GSP_LIST": invalid identifier
ORA-06512: at line 54

Script Terminated on line 5.

----------------------------
Please suggest
how to handle the nested table list in dynamic query.
Tom Kyte
June 28, 2011 - 12:02 pm UTC

smaller, make this MUCH MUCH MUCH smaller.

and make it so we can actually run it too.

stuff like:

CREATE FUNCTION string_tokenizer(p_input_string IN VARCHAR2,
                            p_delim_char   IN CHAR)
  RETURN STRING_ARRAY
  AS
    v_ret_list   STRING_ARRAY;
    v_target     BINARY_INTEGER;
    v_index      BINARY_INTEGER;
    v_this_delim BINARY_INTEGER;
    v_last_delim BINARY_INTEGER;
    --Start local function


  CREATE FUNCTION str_tokenizer(p_input_string IN VARCHAR2,
                            p_delim_char   IN CHAR)
  RETURN CIB_SPDF_STRING_ARRAY
  AS
    v_ret_list   CIB_SPDF_STRING_ARRAY;
    v_target     BINARY_INTEGER;
    v_index      BINARY_INTEGER;
    v_this_delim BINARY_INTEGER;
    v_last_delim BINARY_INTEGER;
    --Start local function
   CREATE FUNCTION splitter_count(str in varchar2, delim in char) return int as
     val int;
    BEGIN


won't compile in the first place.


Make your test case 100% complete YET TINY, TEENY TINY - just enough to reproduce the issue.


I have a feeling the code in question will be:


DECLARE

        cur_get_util SYS_REFCURSOR;
        v_cur_get_util UTIL_TBL%ROWTYPE;
        v_gsp_list          STRING_ARRAY;   
        v_prod_fam_list     STRING_ARRAY;        
        
        Sql_str          Varchar2(4000) := 'INSERT INTO F_TEMP SELECT DISTINCT 
dpg.generic_service_name,
                                   dpf.prodfam_name 
                                   FROM 
                                   dv_prod_gsp dpg,
                                   dv_prod_fam dpf
                                   WHERE dpg.generic_service_item_id = 
dpf.generic_service_item_id
                                   AND dpg.generic_service_name in( SELECT * 
FROM TABLE(v_gsp_list))';



when you call execute immediate on that string, it would be like calling another procedure - your v_gsp_list GOES OUT OF SCOPE, sql cannot see that. You have to bind that stuff in.


If I'm guessing right - then your test case should have been about this big:



ops$tkyte%ORA11GR2> create table t as select * from all_users where 1=0;

Table created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> declare
  2          v_gsp_list   sys.odciVarchar2List := sys.odciVarchar2List( 'SYS', 'SCOTT', user );
  3          l_sql        long := 'insert into t select * from all_users where username in (select * from TABLE(:x))';
  4  begin
  5          execute immediate l_sql using v_gsp_list;
  6  end;
  7  /

PL/SQL procedure successfully completed.

ops$tkyte%ORA11GR2> select * from t;

USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
OPS$TKYTE                             243 17-JUN-11
SCOTT                                  83 05-SEP-10
SYS                                     0 05-SEP-10


and that one shows you how to do it.

nested table -list in a dynamic query

Jitendra Jalota, June 29, 2011 - 12:15 am UTC

Hi Tom,

Thanks a ton reply.
sorry for error in previous code
following line needs to be replaced

END string_tokenizer;
with
END str_tokenizer;

in the previous given code
i am trying to append few more nested table list in where clause dynamically based on the avialability of the values means if the values are available in list so that nested list array will be added in where clause as a condition..if its null so keep it as it is..and execute the dynamic string..i have gone through the solution from your side..will it work for multiple dynamic list used in a query?

Tom Kyte
June 29, 2011 - 12:02 pm UTC

if by multiple dynamic lists, you mean "more than one bind variable", yes, it will work.

nested table -list in a dynamic query

Jitendra Jalota, June 29, 2011 - 4:36 am UTC

Hi Tom,

i tried with multiple array list with execute immediate..

EXECUTE IMMEDIATE SQL_STR USING list1,list2,list3,list4;

its fine but
questions here ..

1.as i mentioned in my previous reply that i am appending the where clause condition for the individual list only if the list.count <>0
so in case any of the list is empty so that list is not added as where clause while dynamic append ..and i do not want to pass the parameter for the empty list..while execute immidiate..


suppose i take the count of all the list..

how to decode on the basis of list count that parameters should be passed or not..

if i am confusing you..could you please go through to my first post in which i have all the code and have the flow at a glance.


Thanks,



Tom Kyte
June 29, 2011 - 12:09 pm UTC

you are confusing me and I need a SMALL CONCISE, yet 100% complete and well formed, example.

No, I'm not going to go through a ton of code, express your situation in as LITTLE code as possible

we don't need any of your splitter/tokenizer code, you just need to set up a teeny tiny example demonstrating your issue - just like I did for you above, in a few lines of code.

Dynamic query with local temporary table

Anuvarman, March 17, 2012 - 12:07 pm UTC

I need temporary table as key pair value using dynamic query with better performance

Tom Kyte
March 19, 2012 - 9:54 am UTC

none of that makes sense.

how do you know that this will result in better performance?

and if you think you need it - why don't you just create it? It is rather simple?

naveen mishra, May 31, 2012 - 4:37 am UTC

Dear tom, i want to hold a stored procedure's result into a variable, and procedure is with in and out parameter, so let me know how could i do this.

Tom Kyte
May 31, 2012 - 9:52 am UTC

so let us know the context here. are you in sqlplus? java? c#? c++? what????


You would in short, in general:

a) create a prepared statement
b) bind variables to the inputs and outputs
c) execute the statement

and the outputs would be in your host (bind) variables.



Quick Example

Jim Shang, June 01, 2012 - 12:10 am UTC

Naveen,
I will have a quick try at answering your question by assuming you are talking about PL/SQL, Tom has a really popular site and to make sure we make the most of the time he allocates here, you should give more detail about your requirements. Hope this helps.

I am using the "emp" table which is a long time demo table provided by Oracle.

Please note, this is a very quick example only.. Depending on what your business rules, data, structures etc are the design of this might be very different

Here is my stored procedure
CREATE OR REPLACE PROCEDURE emp_name (id IN NUMBER, emp_name OUT NUMBER)
IS
BEGIN
    SELECT first_name INTO emp_name
    FROM emp WHERE empID = id;
END;
/


I then call that stored procedure as follows
DECLARE
empName varchar(20);
CURSOR id_cur SELECT empno FROM emp;
BEGIN
  FOR emp_rec in id_cur
  LOOP
    emp_name(emp_rec.empno, empName);
    dbms_output.putline('The employee ' || empName || ' has id ' || emp-rec.empno);
END LOOP;
END;
/


If you run this from SQL*Plus you should see a result like
The employee SMITH has id 7369
The employee ALLEN has id 7499
The employee WARD has id 7521
The employee JONES has id 7566
The employee MARTIN has id 7654
The employee BLAKE has id 7698
The employee CLARK has id 7782
The employee SCOTT has id 7788
The employee KING has id 7839
The employee TURNER has id 7844
The employee ADAMS has id 7876
The employee JAMES has id 7900
The employee FORD has id 7902
The employee MILLER has id 7934

PL/SQL procedure successfully completed.


If nothing gets displayed (assuming there was no errors in your setup) then in SQL*Plus run

SET SERVEROUTPUT ON


This has the effect of
DBMS_OUTPUT.ENABLE (buffer_size => NULL);

Regards
Jim

Correction

Jim Shang, June 01, 2012 - 12:14 am UTC

Sorry... didn't post the right version of the procedure after I corrected it.

emp_name needs to be a varchar variable

correct copy follows
CREATE OR REPLACE PROCEDURE emp_name (id IN NUMBER, emp_name OUT varchar)
IS
BEGIN
    SELECT ename INTO emp_name
    FROM emp WHERE empno = id;
END;
/

Dynamic Select

A reader, August 03, 2012 - 1:32 am UTC

Hi Tom,
can i solve below issue. This code is under table level trigger.I need to compare :new.field_name=:old.filed_name in dynamic way based on all filed from the table.Can you please go thorough below code and commnet. current out put is showing showing :new.deal_qty instead of the value
Regards
mathew



FOR I IN (select ':NEW.'||column_name NEW_V,':OLD.'||column_name OLD_V from cols where table_name='OP_DEALDT')

LOOP
v_new:=I.NEW_V;
plsql_block := 'select '||lower(v_new)||' from dual';
EXECUTE IMMEDIATE plsql_block INTO v_new_val_final ;
if I.NEW_V =':NEW.DEAL_QTY' THEN
RAISE_APPLICATION_ERROR(-20014,v_new_val_final||'xxxx');
end if;
END LOOP;
Tom Kyte
August 15, 2012 - 4:47 pm UTC

there is so much wrong with this - the lack of binds on the "plsql_block" variable being the wrongest.

You cannot dynamically access the :new elements - it is not possible.

http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:734825535375

Execute Immediate to return output

Petta, January 02, 2013 - 3:43 am UTC

Hi Tom,

Please can you help me on below piece of code
DECLARE
v_SPName VARCHAR2(225);
v_StartDate VARCHAR2(11);
v_EndDate VARCHAR2(11);
v_VersionNumber VARCHAR2(1);
v_debug VARCHAR2(1);
v_Return NUMBER;
v_ProcedureCount NUMBER;
v_SQLQuery VARCHAR2(4000);

BEGIN

v_SPName := 'USP_PRIALTDISPENSING';
v_StartDate := NULL; /* If required actual date , then please follow the format as '05-OCT-2012' */
v_EndDate := NULL; /* If required actual date , then please follow the format as '04-NOV-2012' */
v_VersionNumber := NULL; /* We can assign actual version number as 1 */
v_debug := 1;
v_ProcedureCount :=0;
v_SQLQuery:= NULL;

IF v_StartDate IS NULL THEN
v_StartDate := '';
END IF;


IF v_EndDate IS NULL THEN
v_StartDate := '';
END IF;

IF v_VersionNumber IS NULL THEN
v_VersionNumber := '';
END IF;

IF v_debug IS NULL THEN
v_debug := 0;
END IF;

SELECT COUNT(Object_Name)
INTO v_ProcedureCount
FROM USER_PROCEDURES
WHERE UPPER(OBJECT_NAME) = UPPER(v_SPName)
AND UPPER(OBJECT_TYPE) = 'PROCEDURE';

IF v_ProcedureCount >=1 THEN
BEGIN

v_SQLQuery := '
DECLARE
v_Return NUMBER;
BEGIN
'||v_SPName ||'('''||v_StartDate||''','||''''||v_EndDate||''''||','''||To_Number(v_VersionNumber)||''', '''||To_Number(v_Debug)||''', '||'v_Return'||' );
DBMS_OUTPUT.PUT_LINE ( v_Return);
END;
';

--DBMS_OUTPUT.PUT_LINE (v_SQLQuery);

EXECUTE IMMEDIATE v_SQLQuery;

END;

END IF;
END;
How to return and assign the value of v_Return in the scope of complete query and not just limited to the dynamic sql.
Objective :
• Capturing the output parameter while executing the Oracle Procedure dynamically

Tom Kyte
January 04, 2013 - 2:29 pm UTC

DO NOT RELY ON NLS_DATE_FORMATS - i can tell by your comment you were planning on doing that, always use an explicit format!!!


suggest you use


q'|this is my string with 'quotes' in it, easier than without the q'| trick!'


ops$tkyte%ORA11GR2> exec dbms_output.put_line( q'|this is my string with 'quotes' in it, easier than without the q'| trick!|' );
this is my string with 'quotes' in it, easier than without the q'| trick!

PL/SQL procedure successfully completed.




in the future, makes the code A LOT easier to read.


You do know that this code:

IF v_StartDate IS NULL THEN
v_StartDate := '';
END IF;


is identical to this code:

<this space left intentionally blank>

'' is null. You might as well say "if x = 1 then x := 1; end if;" in your code too if you do that (so stop doing that!)

I don't like your "upper"'s in the query either - totally unnecessary and possibly buggy. Just use the right case in the first place.


you are using string concatenation - very bad, not necessary. You seem to have a procedure you want to run dynamically that takes input/outputs of:

start date
end date
version number
debug
return


where the first four are inputs and the last is an output, it would be like this:

ops$tkyte%ORA11GR2> create or replace procedure test_proc
  2  ( p_startDate in date,
  3    p_enddate   in date,
  4    p_version   in number,
  5    p_debug     in number,
  6    p_return    out number
  7  )
  8  as
  9  begin
 10      dbms_output.put_line( 'start date = ' || to_char( p_startDate, 'dd-mon-yyyy hh24:mi:ss' ) );
 11      dbms_output.put_line( 'end   date = ' || to_char( p_startDate, 'dd-mon-yyyy hh24:mi:ss' ) );
 12      dbms_output.put_line( 'version    = ' || p_version );
 13      dbms_output.put_line( 'debug      = ' || p_debug );
 14  
 15      p_return := 42;
 16  end;
 17  /

Procedure created.

ops$tkyte%ORA11GR2> 
ops$tkyte%ORA11GR2> declare
  2      l_spName        varchar2(30) := 'TEST_PROC';
  3      l_StartDate     date := to_date( '05-oct-2012', 'dd-mon-yyyy' );
  4      l_endDate       date := to_date( '04-nov-2012', 'dd-mon-yyyy' );
  5      l_versionNumber number := 1;
  6      l_debug         number := 1;
  7      l_return        number;
  8  begin
  9      for x in (select null
 10                  from user_procedures
 11                 where object_name = l_spName and rownum=1 )
 12      loop
 13          execute immediate
 14          'begin "' || l_spName || '"( :start_date, :end_date, :version_number, :debug, :return ); end;'
 15          using in l_startDate, in l_endDate, in l_versionNumber, in l_debug, out l_return;
 16      end loop;
 17  
 18      dbms_output.put_line( 'return = ' || l_return );
 19  end;
 20  /
start date = 05-oct-2012 00:00:00
end   date = 05-oct-2012 00:00:00
version    = 1
debug      = 1
return = 42

PL/SQL procedure successfully completed.




see how much cleaner it is with bind variables? AND SAFER TOO - you didn't sanitize your inputs - with binds - you *dont have to!*

if you use binds - you cannot be sql injected. The only thing we had to verify was the stored procedure name. And note there that we used a quoted identifier (important, else you are subject to sql injection again!!!) on line 14.


doubt

Boris, March 27, 2013 - 11:59 am UTC

Thanks for your excellent service to the oracle community.

Could you tell me the difference between dbms_sql and ref cursor?


Tom Kyte
March 27, 2013 - 5:21 pm UTC

a dbms_sql cursor is accessed procedurally using the dbms_sql api calls. You can describe a sql statement and discover how many columns it outputs, what their types are, names and so on. You can use dbms_sql to process an arbitrary sql statement that returns an arbitrary number of columns of unknown types at runtime.

a dbms_sql cursor, if returned to a client, must still be processed by dbms_sql (sent back to the database for processing).


a ref cursor on the other hand can be returned to a client and the client would deal with it as if it had opened the cursor itself. So, you do not send the cursor "back" to the database to process it, you can just fetch from it, describe it ,whatever - using the clients native SQL api.


a ref cursor in plsql must return a number of columns of a specific type to be known at compile time. With a ref cursor - a plsql routine must know statically how many columns it returns and their types. There is no way to dynamically describe the columns in a ref cursor result set in plsql.

In 11g there is a method however to convert a dbms_sql cursor to a ref cursor to a dbms_sql cursor. So, you can dynamically process a ref cursor in plsql now by casting it to a dbms_sql cursor type and using the dbms_sql api to process it.


otherwise, both are cursors.

doubt

Boris, March 27, 2013 - 7:51 pm UTC

Thanks a lot Tom... Your response is extremely helpful to understand the constructs in a better way especially novice programmers like me

dynamic binding table name?

andrew, April 12, 2013 - 7:05 am UTC

HI Tom,

I have a need to dynamically bind the table name, otherwise there will be 14,000 similar sqls just difference in table name in v$sql
eg. execute immediate 'select * from :x ' using xxxxx
I've gone through the whole thread, does not seem possible. But is it possible to have a work around?

thanks

andrew
Tom Kyte
April 22, 2013 - 3:04 pm UTC

you cannot bind an identifier.


you would have to use string concatention - so make sure to use dbms_assert and/or query the dictionary to verify the name before adding it to the sql.

each query needs a differently compiled plan, no way around it.

I would be questioning this design need, I would be fighting this design "need" to death. I don't think this design need would last very long.

DBMS_AW. EVAL_NUMBER (and/or maybe even EVAL_TEXT)

Hoek, April 16, 2013 - 9:59 am UTC

Can we consider function DBMS_AW.EVAL_NUMBER as a safe and valid alternative to Dynamic SQL (assuming the requirement fits the capabilities of the function and all other environmental/licensing stuff allows for it) or do you see any caveats?
Would the use of such a function justify native compilation?

Lots of examples on OTN, like:
https://forums.oracle.com/forums/thread.jspa?messageID=9421580�


Tom Kyte
April 22, 2013 - 7:23 pm UTC

I believe that package to be part of the extra cost option Oracle On-Line Analytical Processing (OLAP)

http://docs.oracle.com/cd/E11882_01/license.112/e10594/options.htm#DBLIC151

see
http://asktom.oracle.com/Misc/evaluating-expression-like-calculator.html
for a discussion of this

Thanks!

Hoek, April 25, 2013 - 2:14 pm UTC


Help needed

A, August 20, 2013 - 7:15 pm UTC

Hi Tom,
I have gone through this thread but could not find anything which matches my requirement.

To the stored procedure comma separated numeric values will be passed which I want to use in WHERE clause for the NUMBER column.

I suppose the p_input parameter to the stored procedure should be varchar2. If I pass as varchar then how I do use in the WHERE clause.

I tried using execute immediate but could not.

Can you please help me to write the script?

Structure of Table1
-------------------
col1 number
col2 varchar2
col3 number

col1 - primary key and the table has millions of records.

create or replace procedure DISPLAY_MULT_VALUES ( p_input varchar2, cur_multi out sys_refcursor)
as
rec_type table1%rowtype;
begin
select table1.<column-list> into rec_type
from table1
where table1.col2 in p_input;

<<some logic>>
end;


On SQLPrompt :

variable cur_multi refcursor
DISPLAY_MULT_VALUES('4017,4016,4019',:multi_mpns);
print cur_multi


Tom Kyte
August 28, 2013 - 5:38 pm UTC

Tricky scenario with dynamic sql

Gunjan, September 24, 2013 - 5:06 pm UTC

Context
step 1)I have a procedure that would run to create/populate a temporary table. This table would have data from various tables that would have different primary keys. So no. of columns would be decided depending upon maximum primary keys a table has.
step 2)Data migration happens that updates data that needs to be retained [which was backed -up in temp table]
step 3)A procedure would read temp table and form update queries on the fly to update main table with data present in temp table.

I have facing problem in last step where I have a counter that gets suffixed with columnname to give a unique column name for primary key. I am trying to create a dynamic query to run updates. But I am getting
PLS-00302: component 'PKDATA' must be declared, because pl/sql engine is not concatenating counter to columnname before trying to parse it.
Please give some pointers to crack this.
FOR CCtemp_rec IN CCtemp_cur(v_tableName) LOOP
l_sql := l_sql || CCtemp_rec.col_data ||' where ';

FOR fetchPK_rec IN fetchPK_cur(v_tableName)
LOOP
v_pkdata := CCtemp_rec.PKDATA||''||counter;
l_sql := l_sql || 'CCtemp_rec.PK'|| counter ||' = '||v_pkdata||' and ';
counter := counter + 1;
END LOOP;
dbms_output.put_line(l_sql);
END LOOP;


create table cctemp(
IMPORTDATE TIMESTAMP(6),
TABLENAME VARCHAR2(200),
COLUMNNAME VARCHAR2(200),
COL_DATA VARCHAR2(200),
PK1 VARCHAR2(1000),
PKDATA1 VARCHAR2(1000),
PK2 VARCHAR2(1000),
PKDATA2 VARCHAR2(1000),
...
....);

create or replace PROCEDURE B IS

CURSOR sourceId_cur IS
select table_name, column_name
from user_tab_cols
where table_name like 'RFN%'
and column_name like '%SOURCE%ID%'
and data_type = 'NUMBER' and table_name = 'RFN_FIELD_TYPE';

CURSOR fetchPK_cur(tableName varchar2) IS
SELECT column_name
FROM all_constraints cons, all_cons_columns cols
WHERE cols.table_name = upper(tableName)
AND cons.constraint_type = 'P'
AND cons.constraint_name = cols.constraint_name
AND cons.owner = cols.owner;

CURSOR fetchPKcount_cur(tableName varchar2) IS
SELECT count(*) pkcount
FROM all_constraints cons, all_cons_columns cols
WHERE cols.table_name = upper(tableName)
AND cons.constraint_type = 'P'
AND cons.constraint_name = cols.constraint_name
AND cons.owner = cols.owner;

CURSOR CCtemp_cur(tableName varchar2) IS
SELECT * FROM CCtemp
WHERE tablename = upper(tableName) and rownum < 3;


TYPE t_PK_arr IS TABLE OF VARCHAR2(100) INDEX BY BINARY_INTEGER;
l_PK_arr t_PK_arr;
l_cnt BINARY_INTEGER := 1;
counter BINARY_INTEGER := 1;
v_pkcount number := 0;

CCtemp_rec CCtemp%rowtype;
v_pkdata varchar2(500);
v_tableName varchar2(500);
v_columnName varchar2(500);
v_pkColumns varchar2(500) := '';
create_sql VARCHAR2(10000);
l_sql VARCHAR2(10000);
l_sql_1 VARCHAR2(10000);
client_id number := 805161677;
columnExist number := 0;
BEGIN

OPEN sourceId_cur;
Loop
FETCH sourceId_cur INTO v_tableName, v_columnName;
exit when sourceId_cur%NotFound;

-- chk if table has client_id column and is not an Audit or LOG table
columnExist := 0;
IF (INSTR(v_tableName, 'AUDIT') = 0 AND INSTR(v_tableName, 'LOG') = 0) THEN

SELECT count(*)
into columnExist
FROM user_tab_columns
WHERE table_name = v_tableName
AND column_name = 'CLIENT_ID';

if columnExist = 1 THEN

l_sql := 'Update '||v_tableName||' set '||v_columnName ||' = ';
dbms_output.put_line(l_sql);


FOR CCtemp_rec IN CCtemp_cur(v_tableName) LOOP
l_sql := l_sql || CCtemp_rec.col_data ||' where ';

FOR fetchPK_rec IN fetchPK_cur(v_tableName)
LOOP
v_pkdata := CCtemp_rec.PKDATA||''||counter;
l_sql := l_sql || 'CCtemp_rec.PK'|| counter ||' = '||v_pkdata||' and ';
counter := counter + 1;
END LOOP;
dbms_output.put_line(l_sql);
END LOOP;


-- EXECUTE IMMEDIATE l_sql;
END IF;
END IF;
end loop; -- sourceId_cur
commit;
CLOSE sourceId_cur;
END B;

More to Explore

DBMS_OUTPUT

More on PL/SQL routine DBMS_OUTPUT here