关于 DECLARE Block contains a commented dynamic input variable 的问题

Problems about DECLARE Block contains a commented dynamic input variable

我刚接触 PL/SQL。

我写了一个块来计算圆的半径和周长,如下所示:

SET SERVEROUTPUT ON;

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;
/

在这里你可以看到我评论声明代码radius NUMBER := &enter_value;,然而,当我运行我的脚本在 SQL*PlusSQL Developer,我总是收到像 please enter the value for enter_value.

这样的弹出消息

相反,如果我在declare中删除这个注释,然后把它放在它外面,那么就没有提示了。

SET SERVEROUTPUT ON;

/* to make it more dynamic, I can set 
   radius NUMBER := &enter_value;
*/

CREATE OR REPLACE PROCEDURE cal_circle AS
-- DECLARE

  pi              CONSTANT NUMBER := 3.1415926;
  radius          NUMBER := 3;
  circumference   DECIMAL(4,2) := radius * pi * 2;
  area            DECIMAL(4,2) := pi * radius ** 2;

BEGIN
......

在这里我想澄清一下,当我尝试注释动态变量时,DECLARE 块是否不能接受注释?

谢谢。

您可以使用参数而不是替换变量来允许不同的用户使用不同的 pi 值调用该过程。

我建议为此使用 FUNCTION 而不是 PROCEDURE,但这里有一个示例(也使用 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;
/

那就试试吧:

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

如果您确实需要 COMPILE 具有不同常量值(不推荐)的过程用替换值 pi,您可以先使用 DEFINE 设置替换变量。喜欢 DEFINE pi_value = 3.1415;,然后使用 &pi_value

Update:为什么 SQLPlusSQL Developer 检测 Substitution Variable 并为其请求一个值,即使它在 comment?

中也是如此

TLDR: SQL 客户端必须向服务器发送评论。注释中的预处理替换提供了更大的灵活性并使 SQL 客户端更简单。客户对控制替代行为有很好的支持。没有太多理由在最终代码中使用孤立替换变量。

加长版: 这些工具是数据库客户端——它们有很多功能,但首要的是它们的首要工作是收集输入 SQL,将其传送到数据库服务器并处理获取的数据。

Comments 需要与它们附带的 SQL 语句一起传送到数据库服务器。这是有原因的——所以用户可以在数据库中保存对他们编译的 SQL 代码的评论,当然,还有 compiler hints。

Substitution Variables 不会像评论那样随 SQL 一起传送到服务器。取而代之的是,它们会先 求值,然后将结果 SQLText 发送到服务器。 (您可以看到进入服务器的 SQL 已将其 Substitution Variables 替换为实际值。参见 V$SQLTEXT)。

自服务器 "makes use" 评论以来,它使事情变得更加灵活并简化了 SQLPlus 甚至在评论中替换替换变量的事情。 (如果需要,这可以被覆盖。我将在下面展示)。 SQLPlus,SQLDeveloper, 等等 可以 被设计成忽略注释中的替换变量,但这会减少它们灵活,可能需要更多代码,因为他们需要识别注释并相应地逐行更改它们的行为。我将在下面进一步展示这种灵活性的一些示例。

以这种方式工作的工具没有太多缺点。

假设一个人只想在开发过程中忽略一段代码一分钟,然后快速 运行 一切。如果一个人拥有 DEFINE 一切,即使它没有被使用,或者删除所有注释代码只是为了它可以 运行,那将是很烦人的。因此,这些工具允许您 SET DEFINE OFF; 并忽略变量。

例如,这个 运行 没问题:

SET DEFINE OFF;
--SELECT '&MY_FIRST_IGNORED_VAR' FROM DUAL;
-- SELECT '&MY_SECOND_IGNORED_VAR' FROM DUAL;
SELECT 1919 FROM DUAL;

如果需要在查询中使用 '&' 本身,SQLPlus 允许您选择另一个字符作为替换标记。有很多选项可以控制事物。

如果一个人已经完成了最终 queryprocedure 的开发,那么使用未定义替换的剩余 "orphan" 评论是无效的。开发完成后,孤儿替换应全部删除,剩余的任何内容都应引用有效的 DEFINEd 变量。

这是一个在注释中使用处理 substitution 的示例。 假设您想调整一些性能不佳的 SQL。您可以在 HINT 中(在注释中)使用替换变量,以允许快速更改使用的索引或执行模式等,而无需实际更改查询脚本。

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;

通常,此处针对 TEST_VALUE 的查询通常会在获取数据时使用其 UNIQUE INDEX

SELECT TEST_VALUE FROM TEST_TABLE WHERE TEST_VALUE = 'VALUE-1919';

X 计划:

------------------------------------------------------------------------------------  
| 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 |  
------------------------------------------------------------------------------------ 

但是可以通过提示强制进行全面扫描。通过在提示中(在注释中)使用替换变量,可以允许替换变量的值直接查询执行:

DEFINE V_WHICH_FULL_SCAN = 'TEST_TABLE';
SELECT /*+ FULL(&V_WHICH_FULL_SCAN) */ TEST_VALUE FROM TEST_TABLE WHERE TEST_VALUE = 'VALUE-1919';

此处替换变量(在其注释中)已更改查询执行。

X-计划:

--------------------------------------------------------------------------------  
| 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 |  
-------------------------------------------------------------------------------- 

如果这里有一堆表而不是一个,一个人可以 DEFINE 不同的目标进行全面扫描,并快速评估每个对查询的影响。