【问题标题】:How to view the type of a variable in PL/SQL dynamically?如何动态查看PL/SQL中变量的类型?
【发布时间】:2018-04-12 17:29:29
【问题描述】:

此链接显示如何在 Oracle 中获取过程/函数变量的类型:View Type of a variable

它通过函数“get_plsql_type_name”来实现:

create or replace function get_plsql_type_name
(
    p_object_name varchar2,
    p_name varchar2
) return varchar2 is
    v_type_name varchar2(4000);
begin
    select reference.name into v_type_name
    from user_identifiers declaration
    join user_identifiers reference
        on declaration.usage_id = reference.usage_context_id
        and declaration.object_name = reference.object_name
    where
        declaration.object_name = p_object_name
        and declaration.usage = 'DECLARATION'
        and reference.usage = 'REFERENCE'
        and declaration.name = p_name;

    return v_type_name;
end;
/

alter session set plscope_settings = 'IDENTIFIERS:ALL';

create or replace type my_weird_type is object
(
    a number
);

create or replace procedure test_procedure is
    var1 number;
    var2 integer;
    var3 my_weird_type;
    subtype my_subtype is pls_integer range 42 .. 43;
    var4 my_subtype;
begin
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR1'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR2'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR3'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR4'));
end;
/

begin
    test_procedure;
end;
/

上述方法的问题在于它是静态的,我需要验证一个变量的类型,该变量可以是在过程/函数范围内声明的变量的子类型。

使用上述方法,我得到以下结果。

Create the type and its subtype:

create or replace type my_weird_type is object
(
    a number
) NOT FINAL;

CREATE OR REPLACE TYPE my_weird_subtype UNDER my_weird_type(  
   b number
);
/

创建一个表并填充它:

create table test_my_weird_type(
x my_weird_type,
y my_weird_subtype
);

INSERT INTO test_my_weird_type (x,y) VALUES (my_weird_type(100),my_weird_subtype(100,200));
COMMIT;

函数创建(它有两个 my_weird_type 参数,有时我需要使用它的子类型):

create or replace function test_procedure (
    inn_type my_weird_type,
    out_subtype my_weird_type
) RETURN number is
    var1 number;
    var2 integer;
begin
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR1'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'VAR2'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'INN_TYPE'));
    dbms_output.put_line(get_plsql_type_name('TEST_PROCEDURE', 'OUT_SUBTYPE'));

   return 1;
end;
/

以下查询:

select test_procedure(x,y) from test_my_weird_type;

给出以下输出:

NUMBER
INTEGER
MY_WEIRD_TYPE
MY_WEIRD_TYPE

但是,正确的输出是:

NUMBER
INTEGER
MY_WEIRD_TYPE
MY_WEIRD_SUBTYPE

该函数需要识别正在使用的子类型,因此 功能“get_plsql_type_name”需要改进。有办法吗?

【问题讨论】:

    标签: oracle plsql user-defined-types


    【解决方案1】:

    您无法根据函数规范测试类型,但可以使用IS OF( type ) 运算符或SYS_TYPEID function 测试传入对象的类型:

    SQL Fiddle

    Oracle 11g R2 架构设置

    CREATE type my_weird_type IS OBJECT
    (
      a NUMBER
    ) NOT FINAL
    /
    
    CREATE TYPE my_weird_subtype UNDER my_weird_type
    (
       b NUMBER
    )
    /
    
    CREATE FUNCTION getType(
      i_type my_weird_type
    ) RETURN VARCHAR2
    IS
    BEGIN
      IF i_type IS OF( my_weird_subtype ) THEN
        RETURN 'subtype';
      ELSIF i_type IS OF( my_weird_type ) THEN
        RETURN 'type';
      ELSE
        RETURN 'other';
      END IF;
    END;
    /
    
    CREATE FUNCTION getType2(
      i_type my_weird_type
    ) RETURN VARCHAR2
    IS
      o_type USER_TYPES.TYPE_NAME%TYPE;
    BEGIN
      SELECT type_name
      INTO   o_type
      FROM   user_types
      WHERE  typeid = SYS_TYPEID( i_type );
    
      RETURN o_type;
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        RETURN NULL;
    END;
    /
    
    create table test_my_weird_type(
      value my_weird_type
    )
    /
    
    INSERT INTO test_my_weird_type (value)
    SELECT my_weird_type(1)      FROM DUAL UNION ALL
    SELECT my_weird_subtype(2,3) FROM DUAL UNION ALL
    SELECT NULL                  FROM DUAL
    /
    

    查询 1

    SELECT t.value.a AS a,
           TREAT( t.value AS my_weird_subtype ).b AS b,
           getType( value ),
           getType2( value )
    FROM   test_my_weird_type t
    

    Results

    |      A |      B | GETTYPE(VALUE) |  GETTYPE2(VALUE) |
    |--------|--------|----------------|------------------|
    |      1 | (null) |           type |    MY_WEIRD_TYPE |
    |      2 |      3 |        subtype | MY_WEIRD_SUBTYPE |
    | (null) | (null) |          other |           (null) |
    

    【讨论】:

    • 这是一个非常面向问题的解决方案,而不是通用的解决方案。我的意思是,如果 OP 创建了某种类型,那么(s)他每次都需要修改函数,我想这不是意图。不过,很高兴知道IS OF( type ) 运营商。
    • ok...如果无法获取用户传递的对象类型,我如何知道其属性以便对其进行操作?
    • @XING 你也可以使用SYS_TYPEID函数。
    • @MT0 用SYS_TYPEID 函数做什么。这将返回数字。你能解释一下吗
    • @Siqueira 更新了另一种方法 - 请参阅 this page 以获取与对象一起使用的有用运算符/函数的列表。您似乎有一个XY-problem 并询问您的解决方案而不是您的问题;写一个新问题,问你想做什么,而不是问你认为你是如何解决它的。
    【解决方案2】:

    该函数需要识别正在使用的子类型,因此 函数“get_plsql_type_name”需要改进。有没有 怎么办?

    没有。没有办法。 USER_IDENTIFIERS 显示有关当前用户拥有的存储对象(如(包/过程/功能等)中的标识符的信息。

    Oracle 不为在SQL 范围内创建的独立对象提供任何数据字典以识别TYPESUBTYPE。您最多可以将它们识别为TYPE

    例如,在您的情况下,即使认为它是SUBTYPE,下面的也只会返回TYPE

    SELECT *
      FROM all_objects
     WHERE object_name = 'MY_WEIRD_SUBTYPE'
    

    编辑:

    我能想到的另一种方法是检查您通过的任何Type 是否有SUPERTYPE。如果是这样,则意味着typesubtype

    您可以使用如下查询:

    SELECT 1
      FROM user_types
     WHERE type_name  = 'MY_WEIRD_SUBTYPE'
     and supertype_name is not null;
    

    你可以在你的函数中实现这个特性来检查它是否是SUBTYPE

    【讨论】:

    • 好的...如果无法获取用户传递的对象类型,我如何知道其属性以便对其进行操作?
    • If it is not possible to get the object type that is beeing passed by the user, how can I know its attributes in order to manipulate it - 你不能操纵它。
    【解决方案3】:

    ANYDATAANYTYPE 允许对 Oracle 对象进行完全、动态的控制。这种方法与静态代码分析方法无关。

    例如,此函数返回任何对象输入的真实类型名称:

    create or replace function get_dynamic_type_name(
        p_anydata anydata
    ) return varchar2 is
        v_typecode pls_integer;
        v_anytype anytype;
    
        v_prec        pls_integer;
        v_scale       pls_integer;
        v_len         pls_integer;
        v_csid        pls_integer;
        v_csfrm       pls_integer;
        v_schema_name varchar2(128);
        v_type_name   varchar2(128);
        v_version     varchar2(32767);
        v_numelems    pls_integer;
        v_result pls_integer;
    begin
        v_typecode := p_anydata.getType(v_anytype);
    
        v_result := v_anytype.GetInfo
        (
            prec        => v_prec,
            scale       => v_scale,
            len         => v_len,
            csid        => v_csid,
            csfrm       => v_csfrm,
            schema_name => v_schema_name,
            type_name   => v_type_name,
            version     => v_version,
            numelems    => v_numelems
        );
    
        return v_type_name;
    end get_dynamic_type_name;
    /
    

    在调用函数之前,必须使用AnyData.ConvertObject转换对象:

    select
        get_type_name(AnyData.ConvertObject(x)) x_type,
        get_type_name(AnyData.ConvertObject(y)) y_type
    from test_my_weird_type;
    
    X_TYPE          Y_TYPE
    ------          ------
    MY_WEIRD_TYPE   MY_WEIRD_SUBTYPE
    

    该函数可能不是简单地获取类型名称的最方便的方法。但它演示了如何使用 ANY 类型来实现 PL/SQL 反射和操作对象,而无需提前了解它们。例如,我的答案是基于我的另一个答案here,它演示了如何找到对象的第一个属性。

    ANY 类型很有趣,但应谨慎使用。使用动态 SQL 生成处理数据的静态代码通常更快、更容易,而不是在动态代码中进行所有处理。我尽可能避免使用对象关系数据库功能。让你的架构更聪明,但让你的列保持沉默。

    【讨论】:

    • 谢谢...我将测试您的解决方案并尽快提供反馈!
    猜你喜欢
    • 1970-01-01
    • 2011-06-16
    • 2010-09-20
    • 1970-01-01
    • 2011-04-06
    • 2010-10-16
    • 1970-01-01
    • 2010-09-22
    • 1970-01-01
    相关资源
    最近更新 更多