NO COPY parameters [message #634488] |
Tue, 10 March 2015 15:16 |
|
ppatel87
Messages: 5 Registered: November 2014
|
Junior Member |
|
|
Hi
I have been trying hard to understand the concept of nocopy in plsql
but not able to reach any conclusion
Can some one tell me by giving a simple example of it
|
|
|
|
Re: NO COPY parameters [message #634490 is a reply to message #634488] |
Tue, 10 March 2015 15:32 |
John Watson
Messages: 8930 Registered: January 2010 Location: Global Village
|
Senior Member |
|
|
My advice woud be: do not even think about using NOCOPY. Sure, you can get a tiny saving in memory and performance because it passes the parameters by reference (so no need to copy them, which happens if you pass them by value). However, this is a potential disaster. Google "side effects in C" if you want to take this further.
|
|
|
Re: NO COPY parameters [message #634499 is a reply to message #634490] |
Tue, 10 March 2015 22:36 |
|
Kevin Meade
Messages: 2103 Registered: December 1999 Location: Connecticut USA
|
Senior Member |
|
|
Yes, as John points out, there are only a few reasons to consider using NOCOPY.
Here is a brief description of what it is. As was pointed out by BlackSwan, this is in the manuals.
When you call procedure B from procedure A and you pass to procedure B a parameter, there are two ways to pass it:
1. BY REFERENENCE
2. BY VALUE (the default for PL/SQL)
The default for PL/SQL is #2, BY VALUE. How you pass a variable gives us several points to consider:
1. If you pass BY VALUE (the default), then a copy is made of the value you want to pass and the copy is sent to the called procedure. But if you pass by reference then you send the memory address of where the value lives to the called procedure which means the called procedure can make changes to your original value since there is no copy of it.
2. If you pass BY VALUE then if something bad happens inside the called procedure and you fail back out to the calling procedure, the original value of your variable is untouched because the called procedure was never working on it; it was given a copy of it. So any changes it might have made to the copy before it crapped out on you, are simply forgotten along with the copy.
3. But if you pass BY REFERENCE, you give the called procedure the memory address of your parameter's value telling the called procedure that it should manipulate the value at that location. Thus there is no copy made. If your called procedure makes changes to the parameter and then the called procedure fails, your calling procedure is stuck with whatever the called procedure did to your parameter value. This can cause problems if you intend to try and continue on since the value you started with is gone forever.
The drawback of BY VALUE is for very large parameters. Consider for example if you had large PL/SQL arrays, or COLLECTIONS that you were passing. You would have to make a copy of them before you pass them. If you have a 10MB array, then you have to stop and make a 10MB copy of that array before you can pass it around. If it is an IN-OUT parameter then it needs to be copied back to its original location when your called procedure is done; thus in effect, a second copy. So with vary large variables, BY VALUE can be substantially slower because you are making lots of copies of things. Consider a FOR LOOP that iterates of 1 million records which for each record must make a copy of a 10MB array. That is 10GB of copying. OK one might question such an application design but you get the idea right?
I have first hand experience with this, though it is from a time long gone (back in the 8i days (or maybe even earlier)). I was working with large PL/SQL arrays, and when I switched from BY VALUE to BY REFERNECE, it ran 50X faster since most of the work was in the copying of the arrays, not actually doing work with array values.
To demonstrate, consider this code and its result. As you can see, each procedure is the same, they each will simply add 1 to whatever number you pass in. The calling procedure will pass in 1 in both cases. The question is, what value will your calling procedure see in its variable should the called procedure fail after adding 1 to the input parameter? NOTICE IN PARTICULAR that each of the called procedures fails with an error, and that the calling procedure traps the error and shows you the value of the variable you passed to the called procedure. See how when you pass BY VALUE, your original value is protected from changes made by the failed procedure but when pass BY REFERENCE (eg. NOCOPY) it is not.
create or replace procedure kev_normal (anumber_p in out integer)
as
begin
anumber_p := anumber_p+1;
raise zero_divide;
end;
/
show errors
create or replace procedure kev_nocopy(anumber_p in out nocopy integer)
as
begin
anumber_p := anumber_p+1;
raise zero_divide;
end;
/
show errors
set serveroutput on
create or replace procedure kevp1
as
myvar integer;
begin
myvar := 1;
dbms_output.put_line('start myvar='||myvar);
begin
kev_normal (myvar);
exception when others then
dbms_output.put_line('normal myvar='||myvar);
end;
--
-- my var is still 1 at this point so let us pass it using NOCOPY
--
begin
kev_nocopy (myvar);
exception when others then
dbms_output.put_line('nocopy myvar='||myvar);
end;
end;
/
show errors
exec kevp1
23:22:57 SQL> exec kevp1
start myvar=1
normal myvar=1
nocopy myvar=2
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01
Does this do it for you? Kevin
|
|
|
|
|