【问题标题】:Calling a PLSQL subprogram from within SQL从 SQL 中调用 PLSQL 子程序
【发布时间】:2016-10-20 22:17:27
【问题描述】:

从 Oracle 存储过程中,是否可以从 SQL 语句中调用子程序?也许一个例子是解释它的更好方法。这是一个简单的例子:

DECLARE
  PROCEDURE p_test(in_text VARCHAR2) IS
  BEGIN
    dbms_output.put_line(in_text);
  END;
BEGIN
  SELECT p_test('Test') FROM dual;
END;
/

但是,这会导致 ORA-00904 错误。所以,我不相信我的问题的答案是肯定的。尽管如此,我还是想问。

我真正的程序试图做的是比较一百多对字段。所以,是这样的:

DECLARE
  PROCEDURE p_compare
  (in_old VARCHAR2, in_new VARCHAR2) IS
  BEGIN
    IF in_old <> in_new THEN
      dbms_output.put_line('Mismatch');

      INSERT INTO tbl_mismatch VALUES (in_old, in_new);
    END IF;
  END;

v_old_value_a VARCHAR2(30);
v_new_value_a VARCHAR2(30);
v_old_value_b VARCHAR2(30);
v_new_value_b VARCHAR2(30);

BEGIN
--What I would like to do
SELECT p_compare(old_value_a, new_value_a), p_compare(old_value_b, new_value_b)
FROM (SELECT 'ALPHA' old_value_a, 'ALPHA' new_value_a, 'BETA' old_value_b, 'DELTA' new_value_b FROM dual);

--What I am currently doing
--I have over 100 pairs of fields that I am comparing 
--Declaring two variables for each pair becomes cumbersome
SELECT old_value_a, new_value_a, old_value_b, new_value_b
INTO v_old_value_a, v_new_value_a, v_old_value_b, v_new_value_b
FROM (SELECT 'ALPHA' old_value_a, 'ALPHA' new_value_a, 'BETA' old_value_b, 'DELTA' new_value_b FROM dual);

p_compare(v_old_value_a, v_new_value_a);
p_compare(v_old_value_b, v_new_value_b);
END;
/

那么,如果我正在尝试的可能是不可能的,有没有更好的方法来实现我的最终目标?

【问题讨论】:

  • 你可以这样调用函数,而不是过程。你对SELECT p_test('Test') FROM dual有什么期望?
  • Aleksej,出于示例的目的,我希望 SELECT p_test('Test') FROM dual 返回与 p_test('Test') 相同的内容。在实际代码中,子程序进行比较和一些额外的操作,并将不同的值插入到一个表中。
  • 如果 p_test 是一个过程,它根本不返回任何东西;如果你需要得到一些结果,你需要创建一个返回值的函数
  • 您不能从 SQL 调用过程,因为过程不返回值,如果您考虑一下,您可以从 SQL 语句调用子程序的唯一地方是值是必需的。因此,您可以从 SQL 语句调用 FUNCTIONS,但不能从 PROCEDURES 调用。祝你好运。
  • 那么为什么需要查询呢?定义一个过程并调用它,而不使用选择

标签: oracle plsql


【解决方案1】:

你可以这样简单地调用一个过程:

  SQL> DECLARE
  2      PROCEDURE p_compare(in_old VARCHAR2, in_new VARCHAR2) IS
  3      BEGIN
  4          IF in_old <> in_new
  5          THEN
  6              DBMS_OUTPUT.put_line('Mismatch');
  7
  8              INSERT INTO tbl_mismatch
  9                   VALUES (in_old, in_new);
 10          END IF;
 11      END;
 12  BEGIN
 13      p_compare('AA', 'BB');
 14      p_compare('XX', 'XX');
 15  END;
 16  /
Mismatch

PL/SQL procedure successfully completed.

SQL> select * from tbl_mismatch;

IN_OLD                         IN_NEW
------------------------------ ------------------------------
AA                             BB

如果您要在 SQL 语句中使用函数来调用,这是一种方法:

SQL> CREATE OR REPLACE FUNCTION f_compare(in_old VARCHAR2, in_new VARCHAR2)
  2      RETURN VARCHAR2 IS
  3  BEGIN
  4      IF in_old <> in_new
  5      THEN
  6          RETURN 'Mismatch';
  7      ELSE
  8          RETURN 'Match';
  9      END IF;
 10  END;
 11  /

Function created.

SQL> DECLARE
  2      v_compare_result1                       VARCHAR2(30);
  3      v_compare_result2                       VARCHAR2(30);
  4  BEGIN
  5      SELECT f_compare(old_value_a, new_value_a), f_compare(old_value_b, new_value_b)
  6        INTO v_compare_result1, v_compare_result2
  7        FROM (SELECT 'ALPHA' old_value_a,
  8                     'ALPHA' new_value_a,
  9                     'BETA' old_value_b,
 10                     'DELTA' new_value_b
 11                FROM DUAL);
 12
 13      DBMS_OUTPUT.put_line('Compare 1: ' || v_compare_result1);
 14      DBMS_OUTPUT.put_line('Compare 2: ' || v_compare_result2);
 15  END;
 16  /
Compare 1: Match
Compare 2: Mismatch

PL/SQL procedure successfully completed.

SQL>

【讨论】:

  • 但我正在从表中提取需要比较的值。我宁愿不必为要比较的每个字段声明两个变量。在我的示例中,我有 v_old_value_a/b。好吧,想象一下 v_[old/new]_value_[01-99]。
  • 也许函数是最好的方法。看起来我每次比较只需要一个变量而不是两个。
  • Guess 函数在嵌入到过程中时不起作用,正如我希望的子程序一样。我正在使用供应商应用程序,所以我有点受限。
  • 在 SQL 中,您只能调用存储函数,不能调用块中定义的函数
  • 只要函数是公共的(即在包头中声明),它也可以在同一个 PL/SQL 包中。或者,如果没有太多要处理的行,您可以遍历它们并在循环中使用本地函数。
【解决方案2】:

如果你喜欢使用变量,你必须在声明任何内联函数/过程之前声明它们。

这个有效:

DECLARE

    x NUMBER;

    PROCEDURE p_test(in_text VARCHAR2) IS
    BEGIN
       DBMS_OUTPUT.PUT_LINE(in_text);
    END;

    FUNCTION give_me_five RETURN NUMBER IS
    BEGIN
        RETURN 5;
    END;

BEGIN
    x := give_me_five;
    DBMS_OUTPUT.PUT_LINE( x );

    p_test('Hello World');
END;

但是这个不行:

DECLARE

    FUNCTION give_me_five RETURN NUMBER IS
    BEGIN
        RETURN 5;
    END;

    x NUMBER;

BEGIN
    x := give_me_five;
    DBMS_OUTPUT.PUT_LINE( x );
END;

ORA-06550: line 7, column 2:
PLS-00103: Encountered the symbol "X" when expecting one of the following:

   begin function pragma procedure
ORA-06550: line 13, column 4:
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following:

   end not pragma final instantiable order overriding static
   member constructor map

【讨论】: