You can use a parameter instead of a substitution variable to allow different users to call the procedure with different values of pi.
I'd recommend using a FUNCTION instead of a PROCEDURE for this, but here's an example (Also using a parameter for radius). :
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;
/
Then try it out:
BEGIN
CAL_CIRCLE(3, 3.14);
END;
/
For a circle with radius 3,the circumference is 18.84 and the area is
28.26.Calculated with Pi=: 3.14
BEGIN
CAL_CIRCLE(3, 3.14159);
END;
/
For a circle with radius 3,the circumference is 18.85 and the area is
28.27.Calculated with Pi=: 3.14159
If you really need to actually COMPILE the procedure with different values for its constants (not recommended) with a substituted value of pi, you can set the substitution variable first with DEFINE. like DEFINE pi_value = 3.1415;, then using &pi_value later.
Update: Why do SQLPlus and SQL Developer detect the Substitution Variable and request a value for it, even when it is in a comment?
TLDR: SQL Clients must deliver comments to the server. Preprocessing substitutions in comments gives greater flexibility and keeps the SQL Clients simpler. The clients have good support for controlling substitution behavior. There is not much of a reason to have orphan substitution variables in finalized code.
Longer-Version:
These tools are database clients--they have lots of features but first and foremost their first job is to gather input SQL, deliver it to the database server and handle fetched data.
Comments need to be delivered to the database server with their accompanying SQL statements. There are reasons for this -- so users can save comments on their compiled SQL code in the database, of course, but also for compiler hints.
Substitution Variables are not delivered with the SQL to the server like comments are. Instead they are evaluated first, and the resultant SQLText is sent to the server. (You can see the SQL that gets into the server has its Substitution Variables replaced with real values. See V$SQLTEXT).
Since the server "makes use" of the comments, it makes things more flexible and simplifies things for SQLPlus to replace the Substitution Variables even in comments. (If needed this can be overridden. I'll show that below). SQLPlus,SQLDeveloper, etc could have been designed to ignore Substitution Variables in comments, but that would make them less flexible and perhaps require more code since they would need to recognize comments and change their behavior accordingly, line-by-line. I'll show some example of this flexibility further below.
There is not much of a drawback to the tools working this way.
Suppose one just wants to ignore a chunk of code for a minute during development and quickly run everything. It would be annoying if one had DEFINE everything even though it wasn't used, or to delete all the commented code just so it could run. So these tools instead allow you to SET DEFINE OFF; and ignore the variables.
For example, this runs fine:
SET DEFINE OFF;
--SELECT '&MY_FIRST_IGNORED_VAR' FROM DUAL;
-- SELECT '&MY_SECOND_IGNORED_VAR' FROM DUAL;
SELECT 1919 FROM DUAL;
If one needs to use '&' itself in a query, SQLPlus lets you choose another character as the substitution marker. There are lots of options to control things.
If one has finished developing one's final query or procedure, it isn't a valid situation to have leftover "orphan" comments with undefined-substitutions. When development is complete, orphan substitutions should all be removed, and anything remaining should reference valid DEFINEd variables.
Here's an example that make use of processing substitutions in comments.
Suppose you wanted to tune some poor-performing SQL. You could use a substitution variable in the HINTs (in a comment) to allow for quickly changing which index is used, or execution mode, etc. without needing to actually change the query script.
CREATE TABLE TEST_TABLE_1(TEST_KEY NUMBER PRIMARY KEY,
TEST_VALUE VARCHAR2(128) NOT NULL,
CONSTRAINT TEST_VALUE_UNQ UNIQUE (TEST_VALUE));
INSERT INTO TEST_TABLE
SELECT LEVEL, 'VALUE-'||LEVEL
FROM DUAL CONNECT BY LEVEL <= 5000;
Normally a query predicating against TEST_VALUE here would normally use its UNIQUE INDEX when fetching the data.
SELECT TEST_VALUE FROM TEST_TABLE WHERE TEST_VALUE = 'VALUE-1919';
X-Plan:
------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 66 | 1 (0)| 00:00:01 |
|* 1 | INDEX UNIQUE SCAN| TEST_VALUE_UNQ | 1 | 66 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------
But one can force a full-scan via a hint. By using a substitution variable in the hint (in a comment), one can allow the values of the Substitution Variable to direct query-execution:
DEFINE V_WHICH_FULL_SCAN = 'TEST_TABLE';
SELECT /*+ FULL(&V_WHICH_FULL_SCAN) */ TEST_VALUE FROM TEST_TABLE WHERE TEST_VALUE = 'VALUE-1919';
Here the Substitution Variable (in its comment) has changed the query-execution.
X-Plan:
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 23 | 1518 | 9 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TEST_TABLE | 23 | 1518 | 9 (0)| 00:00:01 |
--------------------------------------------------------------------------------
If there were a bunch of tables here instead of one, a person could DEFINE different targets to full-scan, and evaluate each impact on the query quickly.
DEFINE enter_value = 3.1415;? To use a substitution variable, you'll need to define it before using it (otherwise you'll be prompted)