如何填充具有默认值的用户定义记录?

时间:2016-10-27 15:17:43

标签: oracle plsql oracle11g user-defined-types mod-plsql

TL; DR:

如何声明用户定义的记录类型,以便在我不填充其中一个字段时,该字段将遵循其DataGridComboBoxColumn

详细说明:

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

DisplayMemberPath

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

<DataGridComboBoxColumn x:Name="combo"
                        Header="Glass"
                        DataContext="{StaticResource jarTypesViewSource}"
                        ItemsSource="{Binding}"
                        SelectedValueBinding="{Binding TypeId}"
                        SelectedValuePath="TypeId"
                        DisplayMemberPath="Type"/>

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

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

  

PL / SQL:ORA-00913:值太多

如果我从/* 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指定值时,该字段将默认为option_attributes

4 个答案:

答案 0 :(得分:1)

按照Oracle doc的AFAIK,&#34;要将记录中的所有字段设置为默认值,请为其分配相同类型的未初始化记录&#34;,这是他们的示例:

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;
/

答案 1 :(得分:1)

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

  

如果这两个陈述都成立,那将是疯狂的:1)用户定义的记录允许您定义默认值,2)您必须填充用户定义记录的每个字段。

第一个陈述是真的;第二个仅在您从查询中分配整个记录时才会出现。

The documentation says

  

对于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 into the record variable那么

  

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

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

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

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

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

答案 2 :(得分:0)

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;

答案 3 :(得分: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;
/  

让我们运行test 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.

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