Young **rz.He** over on Stack Overflow has a question about why SQL*Plus doesn't parse every line of a PL/SQL block in case it's a comment in order to avoid unnecessary prompts for substitution variables. Here's his mcve:

SQL> begin 2 null; -- &commented_out 3 end; 4 / Enter value for commented_out: wtf PL/SQL procedure successfully completed.

Oh, wait - it's not that. It's actually this:

CREATE OR REPLACE PROCEDURE cal_circle AS -- DECLARE pi CONSTANT NUMBER := 3.1415926; radius NUMBER := 3; -- to make it more dynamic I can set -- radius NUMBER := &enter_value; circumference DECIMAL(4,2) := radius * pi * 2; area DECIMAL(4,2) := pi * radius ** 2; BEGIN -- DBMS_OUTPUT.PUT_LINE('Enter a valur of radius: '|| radius); dbms_output.put_line('For a circle with radius ' || radius || ',the circumference is ' || circumference || ' and the area is ' || area || '.'); END; /

But anyway.

An excellent answer explains all about clients and servers and procedure parameters and why it's really not SQL*Plus's job to pre-parse everything before sending it to the database and it wouldn't be such a great idea if it did. I voted up. But what really baked my noodle was the opening comment (my emphasis):

You can use a parameter instead of a substitution variable to allow different users to call the procedurewith different values of pi.

...with a fixed version of the code posted as an example...

CREATE OR REPLACE PROCEDURE CAL_CIRCLE ( P_RADIUS IN NUMBER, P_PI IN NUMBER ) AS CIRCUMFERENCE DECIMAL(4, 2) := P_RADIUS * P_PI * 2; AREA DECIMAL(4, 2) := P_PI * P_RADIUS ** 2; BEGIN DBMS_OUTPUT.put_line('For a circle with radius ' || P_RADIUS || ', the circumference is ' || CIRCUMFERENCE || ' and the area is ' || AREA || '. ' || 'Calculated with Pi = ' || P_PI); END;

You will agree that is much more flexible. Now we can call it for a conventional *π*, like this:

SQL> call cal_circle(3, 3.1416); For a circle with radius 3, the circumference is 18.85 and the area is 28.27. Calculated with Pi = 3.1416

Or like this:

SQL> call cal_circle(3, acos(-1)); For a circle with radius 3, the circumference is 18.85 and the area is 28.27. Calculated with Pi = 3.1415926535897932384626433832795028842

But what if we need to switch back to the imperial *π* following our exit from the EU, or the chancellor reduces the UK's *π* in order to stimulate economic growth, or we want to use the biblical *π* for religious reasons, or if we simply want to use the same procedure in an alternative universe with a fundamentally different geometry than our own? No problem:

SQL> call cal_circle(3, 2.71828); For a circle with radius 3, the circumference is 16.31 and the area is 24.46. Calculated with Pi = 2.71828

Now that is reusability. (The radius can't be more than 9.99, but come on, you can't have everything.)

As usual, of course, the easy way is no fun:

create or replace function circumference ( p_radius in number ) return number deterministic as begin return p_radius * 6.2831853071795864769252867666; end circumference;

(with something similar for area - at which point you might consider putting them both into a `circle`

package). Or, slightly more fun:

create or replace type circle as object ( radius number , diameter number , circumference number , area number , constructor function circle(p_radius number) return self as result ); / create or replace type body circle as constructor function circle ( p_radius number ) return self as result is begin self.radius := p_radius; self.diameter := self.radius * 2; self.circumference := self.radius * 6.2831853071795864769252867666; self.area := 3.1415926535897932384626433833 * radius ** 2; return; end; end; / SQL> select circle(3) as mycircle from dual; MYCIRCLE(RADIUS, DIAMETER, CIRCUMFERENCE, AREA) ---------------------------------------------------- CIRCLE(3, 6, 18.8495559, 28.2743339) 1 row selected.

## No comments:

Post a Comment