如何动态查看PL / SQL中变量的类型?

时间:2017-10-31 13:57:33

标签: oracle plsql user-defined-types

此链接显示如何在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”需要改进。有办法吗?

3 个答案:

答案 0 :(得分:2)

您无法根据函数规范测试类型,但可以使用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) |

答案 1 :(得分:1)

  

因此,该函数需要识别所使用的子类型   函数“get_plsql_type_name”需要改进。有没有   这样做的方法?

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

SQL不会为在TYPE范围内创建的独立对象提供任何数据字典,以识别SUBTYPETYPE。您最多可以将其标识为TYPE

例如,在您的情况下,下面的一个只会返回SUBTYPE,甚至认为它是SELECT * FROM all_objects WHERE object_name = 'MY_WEIRD_SUBTYPE'

Type

编辑:

我能想到的另一种方法是检查您传递的SUPERTYPE是否有type。如果是,则意味着subtypeSELECT 1 FROM user_types WHERE type_name = 'MY_WEIRD_SUBTYPE' and supertype_name is not null;

您可以使用以下查询:

SUBTYPE

您可以在函数中实现此功能,以检查其是compile 'com.google.android.gms:play-services:7.8.0' 还是

答案 2 :(得分:1)

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,它演示了如何找到对象的第一个属性。

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