Oracle PL/SQL - 取消引用字符串作为变量

Oracle PL/SQL - Dereference a string as a variable

是否可以在 PL/SQL 中取消引用字符串作为变量?

我正在寻找这样的东西:

declare
    my_var     CONSTANT varchar2(50) := 'test';
    my_var_ref CONSTANT varchar2(50) := 'my_var';

begin
    -- This of course doesn't work, it's just to illustrate what I'm looking for:
    DBMS_OUTPUT.PUT_LINE(&my_var_ref) -- Prints test
end;

将名称作为过程参数传递:

declare
    my_var     CONSTANT varchar2(50) := 'test';
    my_var_ref CONSTANT varchar2(50) := 'my_var';

begin
    my_func(&my_var_ref) -- Pass my_var instead of my_var_ref
end;

[TL;DR] 不,您不能取消引用 PL/SQL 变量。但是,另一种选择可能是使用关联数组。


如果您尝试使用 EXECUTE IMMEDIATE 取消引用它,那么正在动态评估的 PL/SQL 块不知道来自调用块的上下文,因此无法获得取消引用变量的值。

例如:

DECLARE
  my_var      CONSTANT varchar2(50) := 'test';
  my_var_ref  CONSTANT varchar2(50) := 'my_var';
  value       VARCHAR2(50);
  plsql_block VARCHAR2(100) := 'BEGIN :value := ' || my_var_ref || '; END;';
BEGIN
  DBMS_OUTPUT.PUT_LINE( plsql_block );
  EXECUTE IMMEDIATE plsql_block USING IN OUT value;
  DBMS_OUTPUT.PUT_LINE( value );
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE( SQLERRM );
END;
/

输出:

BEGIN :value := my_var; END;
ORA-06550: line 1, column 17:
PLS-00201: identifier 'MY_VAR' must be declared

它获得了正确的变量名,但该变量未在 PL/SQL 块的动态评估发生的上下文中定义,因此它不起作用。

但是,如果您尝试传入变量的值(而不是尝试解除对变量的引用),那么这表明,除了解除引用之外,其余代码都可以工作:

DECLARE
  my_var      CONSTANT varchar2(50) := 'test';
  my_var_ref  CONSTANT varchar2(50) := 'my_var';
  value       VARCHAR2(50);
  plsql_block VARCHAR2(100) := 'BEGIN :value := ''' || my_var || '''; END;';
BEGIN
  DBMS_OUTPUT.PUT_LINE( plsql_block );
  EXECUTE IMMEDIATE plsql_block USING IN OUT value;
  DBMS_OUTPUT.PUT_LINE( value );
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE( SQLERRM );
END;
/

输出:

BEGIN :value := 'test'; END;
test

但是,您可以改用 PL/SQL 关联数组:

DECLARE
  TYPE MAP IS TABLE OF VARCHAR2(50) INDEX BY VARCHAR2(50);
  my_var_ref CONSTANT VARCHAR2(50) := 'my_var';
  v_map MAP;
BEGIN
  v_map('my_var') := 'test';

  DBMS_OUTPUT.PUT_LINE( v_map( my_var_ref) );
END;
/

输出:

test

db<>fiddle here

您可以使用包。

create or replace package test_pkg as

   my_var1 varchar2(50);
   my_var2 varchar2(50);
   my_var3 varchar2(50);

   procedure set_val(p_var in varchar2,
                     p_val in varchar2);

   function get_val(p_var in varchar2)
     return varchar2;

end test_pkg;
/

create or replace package body test_pkg as

   procedure set_val(p_var in varchar2,
                     p_val in varchar2) is
   begin
      execute immediate 'begin
                            test_pkg.' || p_var || ' := :x;
                         end;'
        using p_val;
   end set_val;

   function get_val(p_var in varchar2)
     return varchar2 is
      v_value varchar2(50);
   begin
      execute immediate 'begin
                            :x := test_pkg.' || p_var || ';
                         end;'
        using out v_value;

      return v_value;
   end get_val;

end test_pkg;
/

然后

set serveroutput on
begin
   for i in 1 .. 3 loop
      test_pkg.set_val('my_var' || i, 'Test ' || i);
   end loop;

   for i in 1 .. 3 loop
      dbms_output.put_line('my_var' || i || ' = ' || test_pkg.get_val('my_var' || i));
   end loop;
end;

输出:

my_var1 = Test 1
my_var2 = Test 2
my_var3 = Test 3