Skip to Main Content
  • Questions
  • Writing a procedure in the declaration section of another procedure

Breadcrumb

Question and Answer

Tom Kyte

Thanks for the question, Sanjay.

Asked: August 28, 2000 - 2:00 pm UTC

Last updated: August 11, 2005 - 8:39 am UTC

Version: 8.0.5

Viewed 1000+ times

You Asked

Tom,
Can we write a stored procedure within the declaration
section of another procedure ? If we can, then from a
programming standpoint what is its significance ? I
recently came across some code where the developer
had written a procedure in the declare section of another proc.
i.e
procedure show_report
is
procedure do_break_columns
is
begin
-- some code
end;
procedure do_sum_break_columns
is
begin
end;
l_tot_rows ...
begin
//do some stuff here
end;
This was the first time that I came across such a construct
,and hence the question
Thanks
Sanjay

and Tom said...



It is a method of encapsulation. I would use a package myself and put SHOW_REPORT in the package specification (allowing others to call it) but having do_break_columns and do_sum_break_columns in the body as "hidden" functions.

In a standalone function/procedure like this, the only way to hide this repetitive logic is to declare these "mini" procedures. The programmer realized that they had some code they did over and over and didn't want to inline that code everywhere in the procedure -- rather they put it in one location and used it over and over. I do it myself frequently -- for example I have an execute immediate from v8.0 and before I use that looks like:

create or replace procedure execute_immediate( p_sql in varchar2,
p_name1 in varchar2 default NULL, p_val1 in out varchar2,
p_name2 in varchar2 default NULL, p_val2 in out varchar2,
p_name3 in varchar2 default NULL, p_val3 in out varchar2,
p_name4 in varchar2 default NULL, p_val4 in out varchar2)
is
cursor_name pls_integer default dbms_sql.open_cursor;
ignore pls_integer;

procedure bind( n in varchar2, v in varchar2 )
is begin if ( n is not null ) then
dbms_sql.bind_variable( cursor_name, n, v, 255 );
end if;
end;
procedure val( n in varchar2, v out varchar2 )
is begin if ( n is not null ) then
dbms_sql.variable_value( cursor_name, n, v );
end if;
end;
BEGIN
dbms_sql.parse(cursor_name, p_sql, dbms_sql.native);
bind( p_name1, p_val1 );
bind( p_name2, p_val2 );
bind( p_name3, p_val3 );
bind( p_name4, p_val4 );
ignore := dbms_sql.execute(cursor_name);
val( p_name1, p_val1 );
val( p_name2, p_val2 );
val( p_name3, p_val3 );
val( p_name4, p_val4 );
dbms_sql.close_cursor(cursor_name);
END;
/

I could have put the code found in bind and val INLINE but I found that to be tedious at best -- it was much easier to write the 3 lines of code once and be done with it. Notice how the procedures in the procedure can see not only their variables but all of the local, in scope variables as well (eg: i did not pass cursor_name -- it can just see it).



Rating

  (4 ratings)

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

Comments

Guidlines on scope of procedures/variables.

Kashif, November 04, 2002 - 6:28 pm UTC

Hi Tom,

Are there are any guidelines on the scope of procedures/variables i.e. when and where should one declare them? For example, when is declaring a global procedure/varaible optimal, and when is declaring a local procedure/variable optimal, assuming I have the choice of declaring them anywhere? Any performance considerations here? Is it true that global procedures/variables occupy more space in memory?

I have read in some texts that unless there is the need to declare variables/procedures, they should not be declared 'statically', but rather be declared conditionally, e.g.

if criteria_are_met then
declare
var1 varchar2 (10);
...

Is this a good practice?

Is declaring procedure A within procedure Z different from declaring procedure A outside procedure Z, in the same package? How?

Finally, if one has the option of declaring procedures/functions/variables in the package spec versus in the package body, where should they be declared?

TIA.

Kashif

Tom Kyte
November 05, 2002 - 8:58 am UTC

you only declare variables as global when they need to be -- their very usage dictates that they must persist from call to call.

Otherwise, they should be local.

Just like in any programming language. globals are to be avoided unless they are necessary.


The other thing -- about having multiple nested levels:

if criteria_are_met then
declare
var1 varchar2 (10);
...

is one I never have ascribed to for the simple reason that I never nest blocks like that in any language. Rather, I move that out into a subroutine (modularize the code). I like to keep my routines on a screen -- so I can see the TOP of the routine and the BOTTOM of the routine at a glance. Nested blocks would pretty much obviate that -- so I make them routines which pushes their variables out of the current routine all together.



declaring procedure a within procedure z limits the scope of A to Z. Take that block from above -- if it were not REUSABLE, no other routine would ever call it, you might consider putting it in Z to limit it's scope. Else, put it outside of Z to allow others to reuse it.


You only put in the spec that which OTHERS outside of the package could reasonably want to call. You publish those functions/procedures that you want exposed -- no more, no less.



is global really global

joachim Mayer, July 21, 2003 - 9:20 am UTC

Hi Tom,

thx for the help you are giving programmers out there. I saw this note on global variables from you but I am still confused because oracle seems to hide them somehow. I will show you my example

I create a simple package with contains a variable in the header-section. Should be global I belive.

mwstat@KXXL> create package my_pack as
2 var constant number := 3;
3 end;
4 /

Package created.

mwstat@KXXL> show error
No errors.

And I can use this value var in any other package or procedure like this

mwstat@KXXL> r
1 begin
2 dbms_output.put_line('var:'|| my_pack.var);
3* end;
var:3

PL/SQL procedure successfully completed.

But when I try to use my_pack.var in a view or a query I get an error

mwstat@KXXL> select * from transport_order where order_id = my_pack.var;
select * from transport_order where order_id = my_pack.var
*
ERROR at line 1:
ORA-06553: PLS-221: 'VAR' is not a procedure or is undefined

I was always in the opinion the variables defined in the header are global and global means anyone can see and use them. Does this exclude queries in oracle or where am I mistaken?

I have about 100 constants to work with and I had the plan to put then in a constant-package, in the header so all the other programmers may use them instead of using plain numbers which leads to problems quite often. Is this a good idea or would you counsel to do it different?

Thx in adavance

hannibal


Tom Kyte
July 21, 2003 - 3:21 pm UTC

It is GLOBAL in PL/SQL, "sql" cannot "see" it

Think of PLSQL as a layer on top of SQL. PLSQL can see sql, PLSQL can pass things to SQL, SQL knows not too much about PLSQL.


If the SQL is executed in the context of PLSQL,eg:

begin
for x in ( select * from transport_order where order_id = my_pack.var )
loop
....


it'll work just find, cause PLSQL will see my_pack.var and "bind" it for us.


You can either:

o do all sql in plsql (my response, it is what I do) and keep it in the package
o use an application context and have the programmers call a "init" function to warm up their session cache (an on-login trigger could do it as well) then you can

select * from t where order_id = sys_context( 'my_pack', 'var' );

o use a table and code:

select * from t where order_id =
( select value from lookups where variable = 'VAR' and rownum = 1 );


they are in the order of my preference.

i see ...

joachim mayer, July 22, 2003 - 1:33 am UTC

anything clear... thx tom.

hannibal

How about procedure encapsulation within a package?

Andy Hardy, August 10, 2005 - 1:16 pm UTC

Hi,

I write encapsulated procedures within my packages, e.g.

create or replace package blah as

procedure foo;
procedure foo2;
procedure foo3;
end;

create or replace package body blah
is
produre foo
is
procedure minifoo
is
begin
null;
end minifoo;
begin
null;
end foo;

produre foo2
is
procedure minifoo2
is
begin
null;
end minifoo2;
begin
null;
end foo2;

, etc...

(We use an IDE that prettifies the indentation, etc).

I find this useful in 'scoping' my procedures i.e. if a module only makes sense from the context of foo, then it's defined within foo.

However, some of the other developers say that they sometimes find it difficult to locate the 'real begin' - they know that they should be looking at foo, but there's a lot of stuff to work through before they hit the 'main' code.

Any thoughts on this?


Tom Kyte
August 11, 2005 - 8:39 am UTC

I'm not a personal fan of procedures in procedures.

A hidden procedure in a package, absolutely.

But a procedure in a procedure is a technique I rarely use.

I do think in general it makes it harder to read. The only time I use it is when I really want a standalone stored procedure (a utility designed to be called from a command line)

More to Explore

PL/SQL demos

Check out more PL/SQL tutorials on our LiveSQL tool.

PL/SQL docs

PL/SQL reference manual from the Oracle documentation library