通过部分填充从 Oracle 过程返回对象类型?
Posted
技术标签:
【中文标题】通过部分填充从 Oracle 过程返回对象类型?【英文标题】:Returning Object Type from Oracle Procedure by partially populating it? 【发布时间】:2017-07-12 10:28:44 【问题描述】:我在 Oracle 中创建了一个对象类型,如下所示:
CREATE OR REPLACE TYPE Generic_MDMU_Scrub_Cols_OBJ as Object
(
i_strTypeOfEntry varchar2(50),
ID_SCRUB number,
NM_DATA_COL1 varchar2(50),
NM_DATA_COL2 varchar2(50),
NM_DATA_COL3 varchar2(50),
NM_DATA_COL4 varchar2(50),
NM_DATA_COL5 varchar2(50),
NM_DATA_COL6 varchar2(50),
NM_DATA_COL7 varchar2(50),
NM_DATA_COL8 varchar2(50),
NM_DATA_COL9 varchar2(50),
NM_DATA_COL10 varchar2(50),
NM_DATA_COL11 varchar2(50),
NM_DATA_COL12 varchar2(50),
NM_DATA_COL13 varchar2(50),
NM_DATA_COL14 varchar2(50),
NM_DATA_COL15 varchar2(50),
NM_DATA_COL16 varchar2(50),
NM_DATA_COL17 varchar2(50),
NM_DATA_COL18 varchar2(50),
NM_DATA_COL19 varchar2(50),
NM_DATA_COL20 varchar2(50),
DATA_VAL1 varchar2(50),
DATA_VAL2 varchar2(50),
DATA_VAL3 varchar2(50),
DATA_VAL4 varchar2(50),
DATA_VAL5 varchar2(50),
DATA_VAL6 varchar2(50),
DATA_VAL7 varchar2(50),
DATA_VAL8 varchar2(50),
DATA_VAL9 varchar2(50),
DATA_VAL10 varchar2(50),
DATA_VAL11 varchar2(50),
DATA_VAL12 varchar2(50),
DATA_VAL13 varchar2(50),
DATA_VAL14 varchar2(50),
DATA_VAL15 varchar2(50),
DATA_VAL16 varchar2(50),
DATA_VAL17 varchar2(50),
DATA_VAL18 varchar2(50),
DATA_VAL19 varchar2(50),
DATA_VAL20 varchar2(50),
OLD_DATA_VAL1 varchar2(50),
OLD_DATA_VAL2 varchar2(50),
OLD_DATA_VAL3 varchar2(50),
OLD_DATA_VAL4 varchar2(50),
OLD_DATA_VAL5 varchar2(50),
OLD_DATA_VAL6 varchar2(50),
OLD_DATA_VAL7 varchar2(50),
OLD_DATA_VAL8 varchar2(50),
OLD_DATA_VAL9 varchar2(50),
OLD_DATA_VAL10 varchar2(50),
OLD_DATA_VAL11 varchar2(50),
OLD_DATA_VAL12 varchar2(50),
OLD_DATA_VAL13 varchar2(50),
OLD_DATA_VAL14 varchar2(50),
OLD_DATA_VAL15 varchar2(50),
OLD_DATA_VAL16 varchar2(50),
OLD_DATA_VAL17 varchar2(50),
OLD_DATA_VAL18 varchar2(50),
OLD_DATA_VAL19 varchar2(50),
OLD_DATA_VAL20 varchar2(50)
);
/
现在我想编写一个过程,它将这个对象类型作为输出参数返回给调用 Java 代码。基本上有几个表存储像OLD_DATA_VAL1...OLD_DATA_VAL20
和DATA_VAL1...DATA_VAL120
这样的列,类似地另一个表具有NM_DATA_COL1...NM_DATA_COL20
列,并且根据不同的用例,只有3 或5 个或任意数量的列将具有值,而其他列将为空。例如-OLD_DATA_VAL1,OLD_DATA_VAL2,OLD_DATA_VAL3,DATA_VAL1,DATA_VAL2,DATA_VAL3,NM_DATA_COL1,NM_DATA_COL2,NM_DATA_COL3
有值,其他列为空。我创建了一个元表,它会给我逗号分隔的列名。
现在,当我尝试使用类型或游标时,问题是由于未填充所有值,因此会引发错误。 我的程序是这样的:
create or replace PROCEDURE METADATA_AUTOMATION_MDPR (
in_id_scrub IN NUMBER
)
AS
V_COL_NAMES_CURRENT_SCRUB_DATA VARCHAR2(500);
V_COL_NAMES_OLD_SCRUB_DATA VARCHAR2(500);
V_COL_HEADER_MAP_WORKFLOW VARCHAR2(500);
sqlstmt VARCHAR2(2000);
V_Generic_MDMU_Scrub_Cols Generic_MDMU_Scrub_Cols_OBJ;
SELECT COL_NAMES_CURRENT_SCRUB_DATA, COL_NAMES_OLD_SCRUB_DATA, COL_HEADER_MAP_WORKFLOW INTO V_COL_NAMES_CURRENT_SCRUB_DATA, V_COL_NAMES_OLD_SCRUB_DATA, V_COL_HEADER_MAP_WORKFLOW FROM MDDBO.MDMU_METADATA_AUTOMATION_MDTB WHERE ID_WORKFLOW_MAINTENANCE_MSTR = in_id_wf;
DBMS_OUTPUT.PUT_LINE(V_COL_NAMES_CURRENT_SCRUB_DATA || ' --- ' || V_COL_NAMES_OLD_SCRUB_DATA || ' --- ' || V_COL_HEADER_MAP_WORKFLOW);
sqlstmt:= 'select ' || V_COL_NAMES_CURRENT_SCRUB_DATA || ',' || V_COL_NAMES_OLD_SCRUB_DATA || ',' || V_COL_HEADER_MAP_WORKFLOW || ',AUTO.TYPE_OF_ENTRY i_strTypeOfEntry FROM MDDBO.MDMU_SCRUB_DATA_MDTB DATA, MDDBO.MDMU_SCRUB_LOG_MDTB MSTR, MDDBO.MDMU_MAP_WORK_FLOW_MDTB WKF,
MDDBO.MDMU_METADATA_AUTOMATION_MDTB AUTO WHERE
MSTR.ID_TBL = WKF.ID_WF and
MSTR.id_scrub = :in_id_scrub and MSTR.id_scrub = DATA.id_scrub and
AUTO.ID_WORKFLOW_MAINTENANCE_MSTR = WKF.ID_WF';
DBMS_OUTPUT.PUT_LINE(sqlstmt);
EXECUTE IMMEDIATE sqlstmt
INTO V_Generic_MDMU_Scrub_Cols
USING in_id_scrub;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error '||sqlerrm);
END METADATA_AUTOMATION_MDPR;
但如果是类型,它会抛出如下所示的 Oracle 错误-
error ORA-00932: inconsistent datatypes: expected - got -
一些 Oracle 专家建议使用全局临时表 (GTT),但我相信它们在所有会话中都很常见,在这里不可行。所以,如果我想部分填充,但我不确定这是否是最好的情况,或者可以用对象完成一些事情,比如将未使用的值部分初始化为 null。
更新
从 2 个语句中打印 dbms_ouput 以使事情更清晰
输出1:
DATA_VAL1,DATA_VAL2,DATA_VAL3,DATA_VAL4,DATA_VAL5 --- OLD_DATA_VAL1,OLD_DATA_VAL2,OLD_DATA_VAL3,OLD_DATA_VAL4,OLD_DATA_VAL5 --- NM_DATA_COL1,NM_DATA_COL2,NM_DATA_COL3,NM_DATA_COL4,NM_DATA_COL5
输出2:
select DATA_VAL1,DATA_VAL2,DATA_VAL3,DATA_VAL4,DATA_VAL5,OLD_DATA_VAL1,OLD_DATA_VAL2,OLD_DATA_VAL3,OLD_DATA_VAL4,OLD_DATA_VAL5,NM_DATA_COL1,NM_DATA_COL2,NM_DATA_COL3,NM_DATA_COL4,NM_DATA_COL5,AUTO.TYPE_OF_ENTRY i_strTypeOfEntry FROM MDDBO.MDMU_SCRUB_DATA_MDTB DATA, MDDBO.MDMU_SCRUB_LOG_MDTB MSTR, MDDBO.MDMU_MAP_WORK_FLOW_MDTB WKF,
MDDBO.MDMU_METADATA_AUTOMATION_MDTB AUTO WHERE
MSTR.ID_TBL = WKF.ID_WF and
MSTR.id_scrub = :in_id_scrub and MSTR.id_scrub = DATA.id_scrub and
AUTO.ID_WORKFLOW_MAINTENANCE_MSTR = WKF.ID_WF
主要问题 - 与对象类型中的列数相比,动态选择的列数更少。 (见输出 1,每个也可以有 3 个)
更新 2
现在我主要关心的是如何从由可变数量的选定列形成的动态 sql 中获取值。
使用下面的查询,我可以获取列标题,但不知何故无法获取 col 值。我的查询将只返回一行-
OPEN rc_ FOR sqlstmt using in_id_scrub;
c_ := DBMS_SQL.to_cursor_number(rc_);
DBMS_SQL.DESCRIBE_COLUMNS(c_, col_count_, desc_tab_);
FOR i_ IN 1..col_count_ LOOP
DBMS_OUTPUT.PUT_LINE(desc_tab_(i_).col_name);
DBMS_SQL.DEFINE_COLUMN(c_, i_, desc_tab_(i_).COL_NAME, 2000);
END LOOP;
res := DBMS_SQL.EXECUTE_AND_FETCH(c_, TRUE);
--DBMS_OUTPUT.PUT_LINE(res);
FOR i IN 1..col_count_ LOOP
tab1.EXTEND;
DBMS_SQL.COLUMN_VALUE(c_, i, tab1(tab1.LAST));
--DBMS_SQL.COLUMN_VALUE(c_, i, tab1(i));
--DBMS_SQL.COLUMN_VALUE(c_, i, arr1);
--DBMS_OUTPUT.PUT_LINE(tab1(1));
END LOOP;
DBMS_SQL.CLOSE_CURSOR(c_);
FOR l_row IN 1 .. tab1.COUNT
LOOP
DBMS_OUTPUT.put_line (tab1 (l_row));
END LOOP;
【问题讨论】:
不,GTT 不是对于所有会话都是通用的,它们的数据对于插入它的会话是私有的。 那么是这种情况下的最佳解决方案还是我们仍然可以使用对象类型? 我不知道。通过 PL/SQL 过程在 GTT 中创建的数据对通过不同会话连接的 Java 程序不可用。我对使用 Java 的了解还不够,无法说出是否会这样。 托尼谢谢,请不要考虑 Java。认为我必须在 SqlDeveloper 中本地运行它。我们可以使用 Object 来做到这一点吗?如果可能的话,您可以使用 Object 或使用 GTT 的任何伪代码发布解决方案吗? 重新更新 2:DBMS_SQL.EXECUTE_AND_FETCH 将只返回 1 行;您需要在循环中使用 DBMS_SQL.EXECUTE,然后使用 DBMS_SQL.FETCH_ROWS,直到没有更多行。 【参考方案1】:您的具体问题似乎是这一行:
sqlstmt:= 'select ' || V_COL_NAMES_CURRENT_SCRUB_DATA || ',' || V_COL_NAMES_OLD_SCRUB_DATA || ',' || V_COL_HEADER_MAP_WORKFLOW || ',AUTO.TYPE_OF_ENTRY i_strTypeOfEntry FROM MDDBO.MDMU_SCRUB_DATA_MDTB DATA, MDDBO.MDMU_SCRUB_LOG_MDTB MSTR, MDDBO.MDMU_MAP_WORK_FLOW_MDTB WKF,
MDDBO.MDMU_METADATA_AUTOMATION_MDTB AUTO WHERE
MSTR.ID_TBL = WKF.ID_WF and
MSTR.id_scrub = :in_id_scrub and MSTR.id_scrub = DATA.id_scrub and
AUTO.ID_WORKFLOW_MAINTENANCE_MSTR = WKF.ID_WF';
...据我所知,因为例如V_COL_NAMES_CURRENT_SCRUB_DATA 可能为空?
在这种情况下,一个简单的 NVL 就足够了:
sqlstmt:= 'select ' || NVL(V_COL_NAMES_CURRENT_SCRUB_DATA, 'null as col1') || ...
所以现在代替:
select ,,SOMECOL,...
你会得到:
select null as col1, null as col2, SOMECOL, ...
【讨论】:
没有任何 V_COL_XXX 不会为空。问题是与 Oracle 对象类型相比,它的列数更少。我将使用 DBMS 输出和元表条目更新我的问题,以使其更清晰。 您不能根据您的类型需要动态连接尽可能多的, null as colNN
列吗?
为此,我需要知道列名,我知道通过另一个表具有值的列名,但不存储没有值的列名。能够做到这一点的最佳做法是什么。我希望我们可以用 null 实例化一个对象的所有元素,并且当我们执行 into 子句时,只有我们选择的那些列会得到更新!
对不起,我并没有真正关注你。您正在动态创建一个 SELECT 语句,并且您需要它返回 N 列,其中 N 是您的类型中的属性数。你知道你添加了多少列(比如 M),所以你需要通过添加 ', null as colNN` (N-M) 次来将它“填充”到 N 列?例如如果您的类型有 100 个属性,但您的动态选择只返回 42,那么您知道您需要添加 null as col43, null as col44, ..., null as col100
。所以你可以写一个循环来做到这一点。我错过了什么?
那么问题是默认构造函数不是你需要的吗?如果是这样,那就自己写吧。以上是关于通过部分填充从 Oracle 过程返回对象类型?的主要内容,如果未能解决你的问题,请参考以下文章
从 0jdbc6 JDBCthin 驱动程序调用具有自定义对象返回类型的 Oracle PL/SQL 过程
Oracle DBMS 能否从 Java 存储过程调用返回 Java 对象?