对象类型中 %ROWTYPE 的解决方法

Posted

技术标签:

【中文标题】对象类型中 %ROWTYPE 的解决方法【英文标题】:Workaround for %ROWTYPE in Object type 【发布时间】:2017-02-20 11:00:29 【问题描述】:

我是 PL/SQL 新手,并试图创建一个引用表行的对象:

CREATE OR REPLACE TYPE my_object AS OBJECT(
  table_row my_table%ROWTYPE
);

编译器给了我错误信息 PLS-00329。 好的,现在我知道我不允许引用这样的表格。但是有解决办法吗?

【问题讨论】:

没有。您必须明确列出对象中的每个字段。我不确定“对表格行的引用”是什么意思-您的意思是对象字段与表格列匹配吗?对象有一个字段,其值以某种方式引用表中的特定行? 我们有一些程序可以从不同的表中加载行,现在我想创建一个对象来操作这些行。不同类型的算法总是相同的,所以我希望通过继承来解决这个问题。但我不想列出每个字段,因为在更改表架构的情况下似乎很容易出错。 您确定需要模式级对象类型,而不仅仅是 PL/SQL 记录类型吗? (您可以基于 table%rowtype 定义 PL/SQL 集合,然后批量选择这些集合。)取决于您需要执行的操作。 好吧,我想使用一些面向对象的特性,比如覆盖函数。我一开始不想用 PL/SQL 解决这个问题,但我必须……所以可能我必须列出每个字段。感谢您的帮助! 【参考方案1】:

您必须列出对象类型声明中的所有字段。你提到你担心这容易出错。您可以通过 PL/SQL 块(或将表和对象名称传递到其中的过程)半自动化对象创建。

假设您有一个表定义为;

CREATE TABLE my_table (
  ID NUMBER(38),
  col_1 DATE,
  col_2 NUMBER(3, 2),
  col_3 VARCHAR2(10),
  col_4 CLOB
);

您可以从数据字典中提取列名和数据类型并构建您的对象创建语句:

DECLARE
  v_stmt VARCHAR2(4000);
BEGIN
  v_stmt := 'CREATE OR REPLACE TYPE my_object AS OBJECT(';

  FOR r IN (
    SELECT column_name,
    CAST (data_type || CASE 
      WHEN data_type IN ('VARCHAR', 'VARCHAR2', 'NVARCHAR2', 'RAW', 'CHAR')
        THEN '(' || data_length || ')'
      WHEN data_type IN ('NUMBER')
          AND (data_precision IS NOT NULL OR data_scale IS NOT NULL)
        THEN '(' || data_precision || CASE
          WHEN data_scale > 0 THEN ',' || data_scale
        END || ')'
      END AS VARCHAR2(30)) AS data_type,
      CASE WHEN column_id < MAX(column_id) OVER () THEN ',' END AS comma
    FROM user_tab_columns
    WHERE table_name = 'MY_TABLE'
    ORDER BY column_id
  )
  LOOP
    v_stmt := v_stmt || r.column_name || ' ' || r.data_type || r.comma;
  END LOOP;

  v_stmt := v_stmt || ')';

  dbms_output.put_line(v_stmt); -- just for debugging
  EXECUTE IMMEDIATE v_stmt;
END;
/

如果你愿意,你也可以提取和使用可空标志,但它在这里可能没有用。 (这改编自a describe replacement。可能有一些数据类型没有被正确处理,但它涵盖了常见的数据类型;如果您有带有 UDT 或其他奇怪的列,则需要扩展以正确包含这些数据类型。)

dbms_output 只是显示生成的语句以便于调试:

CREATE OR REPLACE TYPE my_object AS OBJECT(ID NUMBER(38),COL_1 DATE,COL_2 NUMBER(3,2),COL_3 VARCHAR2(10),COL_4 CLOB)

PL/SQL procedure successfully completed.

由于该语句也被执行,对象被创建:

desc my_object;

Name  Null? Type         
----- ----- ------------ 
ID          NUMBER(38)   
COL_1       DATE         
COL_2       NUMBER(3,2)  
COL_3       VARCHAR2(10) 
COL_4       CLOB         

如果您想添加函数,您可以使用生成的 create 语句作为起点,并在手动运行之前对其进行编辑以添加您需要的任何内容,而不是使用 execute immediate

【讨论】:

【参考方案2】:

听起来很方便,但我认为类型不可能有任何这样的语法,因为类型必须提供一个 SQL 接口——你必须能够描述类型并查看它的属性列表,属性需要是在 user_type_attrs 中发布,您必须能够从中创建子类型和对象关系表和列,在查询中使用它并查看列投影等。

当您使用select * 创建视图时,列列表会在创建时展开,但如果您稍后更改表,则不会重新构建。因此,即使 Oracle 确实提供了一种对类型执行类似操作的方法,我怀疑它们也必须遵循相同的模式,即在创建时简单地生成列表,然后将其留给您维护,以避免管理依赖项和级联失效,特别是考虑到 type evolution 周围的限制,当类型本身具有复杂的依赖关系时。

【讨论】:

以上是关于对象类型中 %ROWTYPE 的解决方法的主要内容,如果未能解决你的问题,请参考以下文章

ORACLE中%TYPE和%ROWTYPE的使用

当结构是在 C# 中转换的通用对象的协变类型时,结构无法转换为对象的任何解决方法?

解决打字稿错误的最佳方法 - 类型上不存在属性

java 动手动脑解决问题

泛型函数中对象的Java分配解决方法? [复制]

Java异常如何解决