6

TL; 博士:

如何声明用户定义的记录类型,以便如果我不填充其中一个字段,该字段将兑现它DEFAULT


细节:

在我的包规范中,我定义了以下记录和表类型:

/* set up a custom datatypes that will allow us to pass an array of values into CCD_UI procedures and functions */
TYPE RECORD_OPTION_ATTRIBUTES IS RECORD(
    option_name             VARCHAR2(200)   NOT NULL DEFAULT 'INVALID NAME"', /* default intentionally breaks HTML */
    option_value            VARCHAR2(200)   NOT NULL DEFAULT 'INVALID VALUE"', /* default intentionally breaks HTML */
    option_selected_ind     NUMBER(1)       NOT NULL DEFAULT '0',
    option_class            VARCHAR2(200)   DEFAULT NULL,
    option_attributes       VARCHAR2(200)   DEFAULT NULL
);

TYPE TABLE_OPTION_ATTRIBUTES IS TABLE OF RECORD_OPTION_ATTRIBUTES
    INDEX BY BINARY_INTEGER;

在包体中,我的功能与此非常相似:

PROCEDURE populate_user_defined_table()
AS

    v_criteria_pairs        TABLE_OPTION_ATTRIBUTES;

BEGIN

    SELECT some_column1 AS option_name, some_column2 AS option_value, some_column3 AS selected_ind,
        some_column4 AS option_class
    BULK COLLECT INTO v_criteria_pairs
    FROM Some_Table
    WHERE some_column='whatever';

END;

敏锐的眼睛会注意到我没有在option_attributes字段中插入任​​何值;我只填充了 5 个可用字段中的 4 个。

当我尝试编译这个包时,我从包体中收到以下错误:

PL/SQL: ORA-00913: 值太多

如果我option_attributes从声明中删除该字段RECORD_OPTION_ATTRIBUTES,则该包将编译。

如何声明记录类型,以便如果我不为 指定值option_attributes,该字段将默认为NULL

4

4 回答 4

1
TYPE RECORD_OPTION_ATTRIBUTES IS RECORD(
    option_name             VARCHAR2(200)   NOT NULL DEFAULT 'INVALID NAME"', /* default intentionally breaks HTML */
    option_value            VARCHAR2(200)   NOT NULL DEFAULT 'INVALID VALUE"', /* default intentionally breaks HTML */
    option_selected_ind     NUMBER(1)       NOT NULL DEFAULT '0',
    option_class            VARCHAR2(200)   DEFAULT NULL,
    option_attributes       VARCHAR2(200)   DEFAULT NULL
);

TYPE TABLE_OPTION_ATTRIBUTES IS TABLE OF RECORD_OPTION_ATTRIBUTES
    INDEX BY BINARY_INTEGER;



PROCEDURE populate_user_defined_table()
AS
  CURSOS cur IS -- cursor selecting values without last column
  SELECT some_column1 AS option_name, some_column2 AS option_value,some_column3 AS selected_ind, some_column4 AS option_class
   FROM Some_Table
  WHERE some_column='whatever';

 TYPE t_tmp_arr IS TABLE OF cur%rowtype index by pls_integer;

 v_tmp_arr               t_tmp_arr;
 v_criteria_pairs        TABLE_OPTION_ATTRIBUTES;

BEGIN
  open cur;
  fetch cur bulk collect into v_tmp_arr;
  close cur;
  for i in 1..v_tmp_arr.count loop
    -- it's better to wrap it into a function which accepts one type of record and returns another one        
    v_criteria_pairs(i).option_name := v_tmp_arr(i).option_name;
    v_criteria_pairs(i).option_value := v_tmp_arr(i).option_value;
    v_criteria_pairs(i).option_selected_ind := v_tmp_arr(i).option_selected_ind;
    v_criteria_pairs(i).option_class := v_tmp_arr(i).option_class;
  end loop;
END;
于 2016-11-07T11:17:27.347 回答
1

select [bulk collect] into使用语法时不能。你在评论中说:

如果这两个陈述都为真,那就太疯狂了:1) 用户定义的记录允许您定义默认值,以及 2) 您必须填充用户定义记录的每个字段。

第一个陈述是正确的;仅当您从查询中分配整个记录时,第二个才是正确的。

文档说

对于 RECORD 类型的记录变量,每个字段的初始值为 NULL,除非您在定义类型时为其指定不同的初始值。

因此,如果您创建一个记录变量,则会设置默认值:

declare
  v_rec RECORD_OPTION_ATTRIBUTES;
begin
  dbms_output.put_line(v_rec.option_name ||':'|| v_rec.option_value
    ||':'|| v_rec.option_selected_ind ||':'|| v_rec.option_class
    ||':'|| v_rec.option_attributes);
end;
/

INVALID NAME":INVALID VALUE":0::

PL/SQL procedure successfully completed.

然后,您可以通过单独设置字段值来覆盖默认值。

如果您选择进入记录变量,那么

对于select_list中的每一列,记录变量必须有一个对应的、类型兼容的字段。select_list中的列必须以与记录字段相同的顺序出现。

它没有明确说选择列表中的值不能比记录类型少,但是第二句话暗示了这一点;您碰巧在记录的末尾添加了额外的字段,但没有什么能阻止您将它放在开头,这更明显违反了这一点。没有机制来指定选择列表中的哪一列映射到记录中的哪个字段,因此您必须以相同的顺序提供完全相同的数字、相同的类型。

查询中的值用于填充记录,始终覆盖默认值。您不能不提供字段值。(即使您的查询将列值评估为 null,它仍然会覆盖默认值;如果您的查询确实SELECT null AS option_name, ...会收到 ORA-06502 数字或值错误,因为该字段不为 null)。select into因此,无论是否使用,您的默认设置都不适用bulk collect

不幸的是,您将添加带有额外字段的新记录和表类型(您将无法将其传递给期望原始类型的过程,因此这可能不实用;您可以添加翻译功能,但这只是情况更糟),或者正如@MartinSchapendonk 建议的那样,接受打击并修改您现有的代码。

您可能不需要更改仅处理集合/记录的任何内容,因为它们不会查看新字段 - 尽管您可能会进行一些修改,或者根本没有该字段的意义。而且您不需要更改直接构造记录的任何内容,因为它们将获得默认的空值,即使这是在游标循环中(不会提取到记录变量中)。您只需 (!) 使用 、 或 更改从 SQL 查询填充集合/记录select intoselect bulk collect into方式fetch into

于 2016-11-02T13:53:11.210 回答
1

根据Oracle doc AFAIK ,“要将记录中的所有字段设置为默认值,请为其分配相同类型的未初始化记录”,这是他们的示例:

DECLARE
   TYPE RecordTyp IS RECORD (field1 NUMBER, 
                         field2 VARCHAR2(32) DEFAULT 'something');
   rec1 RecordTyp;
   rec2 RecordTyp;
BEGIN
-- At first, rec1 has the values you assign.
   rec1.field1 := 100; rec1.field2 := 'something else';
-- Assigning an empty record to rec1
-- resets fields to their default values.
-- Field1 is NULL and field2 is 'something'
-- due to the DEFAULT clause
   rec1 := rec2;
   DBMS_OUTPUT.PUT_LINE
     ('Field1 = ' || NVL(TO_CHAR(rec1.field1),'<NULL>') || ',
      field2 = ' || rec1.field2);
END;
/
于 2016-10-31T13:22:08.020 回答
0

这是另一个对象类型的选项,它不是 PL/SQL 记录,但具有相同的行为以及更多用于在构造函数中使用默认值进行初始化的选项(使用表达式和 PL/SQL 函数):

用构造函数定义新类型:

CREATE OR REPLACE TYPE RECORD_OPTION_ATTRIBUTES AS OBJECT(
  option_name             VARCHAR2(200),
  option_value            VARCHAR2(200),
  option_selected_ind     NUMBER(1),
  option_class            VARCHAR2(200),
  option_attributes       VARCHAR2(200),
  constructor function RECORD_OPTION_ATTRIBUTES(
    in_option_name             VARCHAR2    DEFAULT 'INVALID NAME"', /* default intentionally breaks HTML */
    in_option_value            VARCHAR2    DEFAULT 'INVALID VALUE"', /* default intentionally breaks HTML */
    in_option_selected_ind     NUMBER      DEFAULT '0',
    in_option_class            VARCHAR2    DEFAULT NULL,
    in_option_attributes       VARCHAR2    DEFAULT NULL
  )
  return self as result
);

在构造函数中使用默认值并且可以使用复杂的初始化逻辑。请记住,您可以有多个构造函数。

create or replace type body RECORD_OPTION_ATTRIBUTES 
as
  constructor function RECORD_OPTION_ATTRIBUTES(
    in_option_name             VARCHAR2    DEFAULT 'INVALID NAME"', /* default intentionally breaks HTML */
    in_option_value            VARCHAR2    DEFAULT 'INVALID VALUE"', /* default intentionally breaks HTML */
    in_option_selected_ind     NUMBER      DEFAULT '0',
    in_option_class            VARCHAR2    DEFAULT NULL,
    in_option_attributes       VARCHAR2    DEFAULT NULL
  )
  return self as result
  as
  begin
    self.option_name         := in_option_name;
    self.option_value        := in_option_value;
    self.option_selected_ind := in_option_selected_ind;
    self.option_class        := in_option_class;
    self.option_attributes   := in_option_attributes;  
    return;
  end;
end;
/  

让我们运行测试sql:

select RECORD_OPTION_ATTRIBUTES(table_name, tablespace_name, ini_trans) 
  from all_tables
 where owner = 'SYS'
   and rownum <= 10;

检查结果:

RECORD_OPTION_ATTRIBUTES(TABLE_NAME,TABLESPACE_NAME,INI_TRANS)(OPTION_NAME, OPTI
--------------------------------------------------------------------------------
RECORD_OPTION_ATTRIBUTES('WRR$_REPLAY_CALL_FILTER', 'SYSAUX', 1, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('AW$EXPRESS', 'SYSAUX', 4, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('AW$AWMD', 'SYSAUX', 4, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('AW$AWCREATE', 'SYSAUX', 4, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('AW$AWCREATE10G', 'SYSAUX', 4, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('AW$AWXML', 'SYSAUX', 4, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('AW$AWREPORT', 'SYSAUX', 4, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('DUAL', 'SYSTEM', 1, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('SYSTEM_PRIVILEGE_MAP', 'SYSTEM', 1, NULL, NULL)
RECORD_OPTION_ATTRIBUTES('TABLE_PRIVILEGE_MAP', 'SYSTEM', 1, NULL, NULL)

10 rows selected.

如您所见,最后两列具有默认值(在本例中为 null)。与问题中的原始 sql 查询相比,您需要做的就是使用 RECORD_OPTION_ATTRIBUTES() 包装选定的列。

于 2016-11-08T11:35:42.153 回答