Oracle:不一致的数据类型问题
Posted
技术标签:
【中文标题】Oracle:不一致的数据类型问题【英文标题】:Oracle: Inconsistent Datatype Issue 【发布时间】:2011-06-10 15:16:35 【问题描述】:我收到不一致的数据类型错误消息,我不知道为什么。我需要一些指导来解决这个问题。
我正在创建两种类型:
我的 Universe 表具有列类型以下列:
Column Name Data Type
PON VARCHAR2(25 BYTE)
RPON VARCHAR2(25 BYTE)
SUPPLIER_NAME VARCHAR2(255 BYTE)
SUB_SUPPLIER_NAME VARCHAR2(255 BYTE)
SOURCE_NO VARCHAR2(40 BYTE)
CKR VARCHAR2(200 BYTE)
LEC_ID VARCHAR2(200 BYTE)
ICSC VARCHAR2(10 BYTE)
ACTL_ST VARCHAR2(10 BYTE)
ADW_ST VARCHAR2(10 BYTE)
PROJ_ID VARCHAR2(100 BYTE)
MOVE_TO_INV_DT DATE
IE_DT DATE
DDD_DT DATE
EFF_BILL_DT DATE
ACTION VARCHAR2(10 BYTE)
SERVICE VARCHAR2(10 BYTE)
AFP VARCHAR2(10 BYTE)
ACNA VARCHAR2(10 BYTE)
SERVICE_NAME VARCHAR2(255 BYTE)
UPLOAD_DT DATE
PROGRAM VARCHAR2(50 BYTE)
INITIATIVE_ID NUMBER
ACOST NUMBER
ACOST_IND VARCHAR2(25 BYTE)
MAPFILE VARCHAR2(100 BYTE)
行类型
create or replace
TYPE test_COMP_REPORT_ROW_TYPE AS OBJECT (
PON VARCHAR2(25 BYTE),
RPON VARCHAR2(25 BYTE),
VENDOR VARCHAR2(255 BYTE),
SUB_SUPPLIER VARCHAR2(255 BYTE),
SOURCE_NO VARCHAR2(40 BYTE),
ATT_CKT_ID VARCHAR2(200 BYTE),
LEC_ID VARCHAR2(200 BYTE),
ICSC VARCHAR2(10 BYTE),
STATE VARCHAR2(10 BYTE),
PROJECT_ID VARCHAR2(100 BYTE),
ACTION VARCHAR2(10 BYTE),
SERVICE_SPEED VARCHAR2(10 BYTE),
SERVICE_NAME VARCHAR(255 BYTE),
INEFFECT_DATE DATE,
EVENT_DATE DATE,
DUE_DATE DATE,
ACOST NUMBER
)
标签类型
create or replace type test_COMP_REPORT_TAB_TYPE
AS TABLE OF test_COMP_REPORT_ROW_TYPE
这是使用这种类型的函数:
create or replace
FUNCTION test_comp_report_func
(
start_dt_h IN VARCHAR2 DEFAULT NULL,
end_dt_h IN VARCHAR2 DEFAULT NULL,
year_h IN VARCHAR2 DEFAULT NULL )
RETURN test_comp_report_tab_type pipelined
IS
e_sql LONG;
program_v VARCHAR2(10);
v_row test_comp_report_row_type := test_comp_report_row_type(NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
);
TYPE rectyp IS REF CURSOR;
rrc_rectyp rectyp;
TYPE recordvar IS RECORD
(
PON VARCHAR2(25 BYTE),
RPON VARCHAR2(25 BYTE),
VENDOR VARCHAR2(255 BYTE),
SUB_SUPPLIER VARCHAR2(255 BYTE),
SOURCE_NO VARCHAR2(40 BYTE),
ATT_CKT_ID VARCHAR2(200 BYTE),
LEC_ID VARCHAR2(200 BYTE),
ICSC VARCHAR2(10 BYTE),
STATE VARCHAR2(10 BYTE),
PROJECT_ID VARCHAR2(100 BYTE),
ACTION VARCHAR2(10 BYTE),
SERVICE_SPEED VARCHAR2(10 BYTE),
SERVICE_NAME VARCHAR(255 BYTE),
INEFFECT_DATE DATE,
EVENT_DATE DATE,
DUE_DATE DATE,
ACOST NUMBER
);
res_rec recordvar;
BEGIN
e_sql := e_sql || 'SELECT
PON,
RPON,
SUPPLIER_NAME VENDOR,
SUB_SUPPLIER_NAME SUB_SUPPLIER,
SOURCE_NO,
CKR,
LEC_ID,
ICSC,
ACTL_ST,
ADW_ST STATE,
PROJ_ID,
ACTION,
SERVICE SPEED,
AFP,
ACNA,
SERVICE_NAME,
IE_DT,
MOVE_TO_INV_DT EVENTDAT,
DDD_DT DUEDATE,
EFF_BILL_DT,
ACOST
FROM UNIVERSE
WHERE to_date(IE_DT) between to_date(nvl(''01/01/2000'', ''01/01/2000''), ''MM/DD/YYYY'')
and to_date(nvl(''12/31/2009'', to_char(trunc(add_months(sysdate, 12),''year'')-1,''MM/DD/YYYY'')), ''MM/DD/YYYY'')
AND PROGRAM = ''T45sONNET''
AND nvl(trim(ACOST_IND), ''NULL'') not in (''INVALID-2005'')
ORDER BY ACTION';
dbms_output.put_line(e_sql);
OPEN rrc_rectyp FOR e_sql;
LOOP
FETCH rrc_rectyp INTO res_rec;
EXIT WHEN rrc_rectyp%NOTFOUND;
v_row.PON := res_rec.PON;
v_row.RPON := res_rec.RPON;
v_row.VENDOR := res_rec.VENDOR;
v_row.SUB_SUPPLIER := res_rec.SUB_SUPPLIER;
v_row.SOURCE_NO := res_rec.SOURCE_NO;
v_row.ATT_CKT_ID := res_rec.ATT_CKT_ID;
v_row.LEC_ID := res_rec.LEC_ID;
v_row.ICSC := res_rec.ICSC;
v_row.STATE := res_rec.STATE;
v_row.PROJECT_ID := res_rec.PROJECT_ID;
v_row.ACTION := res_rec.ACTION;
v_row.SERVICE_SPEED := res_rec.SERVICE_SPEED;
v_row.SERVICE_NAME := res_rec.SERVICE_NAME;
v_row.INEFFECT_DATE := res_rec.INEFFECT_DATE;
v_row.EVENT_DATE := res_rec.EVENT_DATE;
v_row.DUE_DATE := res_rec.DUE_DATE;
v_row.ACOST := res_rec.ACOST;
pipe ROW(v_row);
END LOOP;
return;
end test_comp_report_func;
我已尝试调试问题,但仍然无法找到出路,如果 SO 社区可以指导,我将不胜感激。
【问题讨论】:
有什么线索或建议可以解决这个问题吗?UNIVERSE.PON
的数据类型是什么?
LONG 有很多问题,您可能应该对 E_SQL 使用类似 varchar2(32767) 的东西。
@jonearles - 为什么会有一些问题?
@Rachel:LONGs 已经被 LOBs 取代了,而且它们已经有十年或两年没有更新了。他们有很多限制,这个页面有一些例子:oracle-developer.net/display.php?id=430
【参考方案1】:
我首先写了一个答案,试图重现你的错误,但你已经改变了你的问题,所以我从头开始。
先说几句:
根据您自己的说法,您是 PL/SQL 的新手,但您正在使用非常高级的功能:动态 SQL、流水线函数、SQL 对象。让我们首先尝试从更简单的东西开始(我将向您展示如何使用静态 SQL,这在 99.9% 的情况下就足够了)。 当您遇到问题时,您需要分解您的代码以查看哪些有效,哪些无效。这通常意味着简化您的代码,直到它变得简单到可以开始工作,然后将代码中的复杂元素一一恢复,直到您再次遇到问题。 当你提供一个测试用例时,尽量让它简单:) 人们会更容易帮助你,但更重要的是在大多数情况下,构建测试用例会帮助你找到解决方案你自己,因为这将迫使你分解你的复杂代码(见前一点)。我的经验法则 (FWIW) 是,在 SO 中使用滚动条(水平或垂直)显示的代码对于测试用例来说太大了,如果可能,需要对其进行修剪。我运行了您的代码并在fetch
行上得到了 ORA-00932。当我用静态 SQL 替换 SQL 时,错误更加明确:
SQL> CREATE OR REPLACE FUNCTION test_comp_report_func
2 RETURN test_comp_report_tab_type
3 PIPELINED IS
4 TYPE recordvar IS RECORD(
5 PON VARCHAR2(25 BYTE),
(...snip...)
21 ACOST NUMBER);
22 res_rec recordvar;
23 v_row test_COMP_REPORT_ROW_TYPE;
24 CURSOR rrc_rectyp IS
25 SELECT PON,
(...snip...)
45 ACOST
46 FROM UNIVERSE;
48 BEGIN
49 OPEN rrc_rectyp;
50 LOOP
51 FETCH rrc_rectyp
52 INTO res_rec;
54 EXIT WHEN rrc_rectyp%NOTFOUND;
55 /*...*/
56 PIPE ROW(v_row);
57 END LOOP;
58 RETURN;
59 END test_comp_report_func;
60 /
Warning: Function created with compilation errors.
LINE/COL ERROR
-------- -----------------------------------------------------------------
51/7 PL/SQL: SQL Statement ignored
52/15 PLS-00386: type mismatch found at 'RES_REC' between FETCH cursor
and INTO variables
这里的问题在于您的选择语句的列数与记录中的字段数不同。您可以使用%rowcount
来防止这种情况:
CREATE OR REPLACE FUNCTION test_comp_report_func
RETURN test_comp_report_tab_type
PIPELINED IS
v_row test_COMP_REPORT_ROW_TYPE;
CURSOR rrc_rectyp IS
SELECT PON, RPON, SUPPLIER_NAME VENDOR, SUB_SUPPLIER_NAME SUB_SUPPLIER,
SOURCE_NO, CKR, LEC_ID, ICSC, ACTL_ST, ADW_ST STATE, PROJ_ID,
ACTION, SERVICE SPEED, AFP, ACNA, SERVICE_NAME, IE_DT,
MOVE_TO_INV_DT EVENTDAT, DDD_DT DUEDATE, EFF_BILL_DT, ACOST
FROM UNIVERSE;
res_rec rrc_rectyp%ROWTYPE;
BEGIN
OPEN rrc_rectyp;
LOOP
FETCH rrc_rectyp
INTO res_rec;
EXIT WHEN rrc_rectyp%NOTFOUND;
v_row.pon := res_rec.pon;
/*...*/
PIPE ROW(v_row);
END LOOP;
RETURN;
END test_comp_report_func;
您甚至可以直接获取 SQL 对象(使用隐式游标):
CREATE OR REPLACE FUNCTION test_comp_report_func
RETURN test_comp_report_tab_type
PIPELINED IS
BEGIN
FOR res_rec IN (SELECT test_comp_report_row_type(PON, RPON, SUPPLIER_NAME,
SUB_SUPPLIER_NAME,SOURCE_NO,
CKR, LEC_ID, ICSC, ACTL_ST,
PROJ_ID, ACTION, SERVICE,
SERVICE_NAME, IE_DT, DDD_DT,
EFF_BILL_DT, ACOST)my_object
FROM UNIVERSE) LOOP
PIPE ROW(res_rec.my_object);
END LOOP;
RETURN;
END test_comp_report_func;
【讨论】:
我也做过类似的事情。你能建议一些我在试图在这里找到问题时应该强调的一些建议吗? @Rachel:你能提供一个完整的测试用例,它实际上会导致 ORA-00932 吗?select * from table(test_comp_report_func('01/01/2000','12/31/2009', null))
,如果您看到我的函数,它需要开始日期、结束日期和年份,并且在查询中没有在任何地方使用年份值。另外,当我直接从 sql 开发人员编辑器运行查询时,可以获取数据,但是当我使用上面的 select 语句调用函数时,它给了我提到的错误。
让我用正在使用的实际值更新问题,这样您就可以最终重现错误。
我已经更新了问题,现在打开 rec_cursor 后,LOOP
出现 ORA-00932 错误,不知道为什么?任何建议。【参考方案2】:
您收到错误是因为e_sql
中的 SQL 查询返回的值比res_rec
中的值多四个。游标返回 21 列数据,但您的 recordvar
记录类型仅包含 17 个字段。
在我看来,ACTL_ST
、AFP
、ACNA
和 EFF_BILL_DT
列不映射到 res_rec
中的任何内容,如果您从查询中删除这些列,您应该会发现您的函数不再报inconsistent datatypes
错误。
我可能会实现类似以下的功能:
CREATE OR REPLACE FUNCTION test_comp_report_func_2 (
start_dt_h IN VARCHAR2 DEFAULT NULL,
end_dt_h IN VARCHAR2 DEFAULT NULL,
year_h IN VARCHAR2 DEFAULT NULL
) RETURN test_comp_report_tab_type PIPELINED
IS
CURSOR cur_res_rec IS
SELECT PON,
RPON,
SUPPLIER_NAME VENDOR,
SUB_SUPPLIER_NAME SUB_SUPPLIER,
SOURCE_NO,
CKR ATT_CKT_ID,
LEC_ID,
ICSC,
ACTL_ST,
ADW_ST STATE,
PROJ_ID AS PROJECT_ID,
ACTION,
SERVICE SERVICE_SPEED,
AFP,
ACNA,
SERVICE_NAME,
IE_DT INEFFECT_DATE,
MOVE_TO_INV_DT EVENT_DATE,
DDD_DT DUE_DATE,
EFF_BILL_DT,
ACOST
FROM UNIVERSE
WHERE TO_DATE(IE_DT) BETWEEN TO_DATE(NVL('01/01/2000', '01/01/2000'), 'MM/DD/YYYY')
AND TO_DATE(NVL('12/31/2009', TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, 12),'year') - 1,'MM/DD/YYYY')), 'MM/DD/YYYY')
AND PROGRAM = 'T45sONNET'
AND NVL(TRIM(ACOST_IND), 'NULL') NOT IN ('INVALID-2005')
ORDER BY ACTION;
v_row test_comp_report_row_type := test_comp_report_row_type(NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
BEGIN
FOR res_rec IN cur_res_rec
LOOP
v_row.PON := res_rec.PON;
v_row.RPON := res_rec.RPON;
v_row.VENDOR := res_rec.VENDOR;
v_row.SUB_SUPPLIER := res_rec.SUB_SUPPLIER;
v_row.SOURCE_NO := res_rec.SOURCE_NO;
v_row.ATT_CKT_ID := res_rec.ATT_CKT_ID;
v_row.LEC_ID := res_rec.LEC_ID;
v_row.ICSC := res_rec.ICSC;
v_row.STATE := res_rec.STATE;
v_row.PROJECT_ID := res_rec.PROJECT_ID;
v_row.ACTION := res_rec.ACTION;
v_row.SERVICE_SPEED := res_rec.SERVICE_SPEED;
v_row.SERVICE_NAME := res_rec.SERVICE_NAME;
v_row.INEFFECT_DATE := res_rec.INEFFECT_DATE;
v_row.EVENT_DATE := res_rec.EVENT_DATE;
v_row.DUE_DATE := res_rec.DUE_DATE;
v_row.ACOST := res_rec.ACOST;
PIPE ROW(v_row);
END LOOP;
RETURN;
END test_comp_report_func_2;
/
首先,老实说,我看不出您使用动态 SQL 的原因。上面的函数使用“静态”SQL 查询,它的优点是 Oracle 在编译函数时会检查该查询是否有效。如果查询有错误,该函数将无法编译。另一方面,如果动态 SQL 查询出现错误,则在运行函数之前不会发现问题。
如果您想更改查询的结构,例如,动态 SQL 很有用。在不同的表上运行它或更改WHERE
子句中使用的列。但是,大多数时候您不需要这样做。如果您不需要使用动态 SQL,那么您真的不应该使用它。
另外,通过使用FOR some_record IN some_cursor
,我不必摆弄打开和关闭游标,也不需要检查是否还有剩余数据,如果有则退出循环。它还减少了必须为行记录 (res_rec
) 声明变量或获取此变量的类型错误的情况。这一切都是自动为我完成的。
【讨论】:
感谢卢克提供的信息。正如我在其他评论中提到的那样,我对 PL/SQL 还很陌生,因此不知道什么是最佳实践以及在哪种情况下使用哪一个,但这肯定会有所帮助,并有助于增加一些知识。跨度>以上是关于Oracle:不一致的数据类型问题的主要内容,如果未能解决你的问题,请参考以下文章
pandas df.to_sql 到 Oracle 数据库数据类型不一致
Oracle 00932. 00000 - “不一致的数据类型:预期的 %s 得到了 %s”