[英]Escaping characters in select statement withing a cursor in Oracle
I have a Function defined in a package that uses a REF CURSOR and IF ELSE logic to assign a select statement to the cursor. Function 执行时出现 ORA-01722:无效编号错误。 我相信这是由于转义字符中的错误:
FUNCTION getReportData(
P_DATE_FROM IN DATE,
P_DATE_TO IN DATE,
PERIOD_TYPE IN INTEGER)
RETURN RPTTCI1328_TABLE PIPELINED IS TYPE cursorOutput IS REF CURSOR;
CUR_ROW RPTTCI1328_ROW;
cur_getByPeriodFilter cursorOutput;
c_stmt_str VARCHAR2 (4000);
BEGIN
IF PERIOD_TYPE = 1 THEN
c_stmt_str :=
' SELECT TO_CHAR(tcis_case.offence_datetime, ''yyyy'') YEAR, tcis_case.status, COUNT(tcis_case.ticket_no) TICKET_NO, ' ||
' SUM(tcis_part_payment.AMT_ORIGINAL) ORIGINAL, ' ||
' SUM(tcis_part_payment.AMT_PAID) PAID, ' ||
' SUM(CASE WHEN tcis_person.reason_code = ''A'' THEN 1 ELSE 0 END) A, ' ||
' SUM(CASE WHEN tcis_person.reason_code = ''D'' THEN 1 ELSE 0 END) D, ' ||
' SUM(CASE WHEN tcis_person.reason_code = ''F'' THEN 1 ELSE 0 END) F, ' ||
' SUM(CASE WHEN tcis_person.reason_code = ''N'' THEN 1 ELSE 0 END) N, ' ||
' SUM(CASE WHEN tcis_person.reason_code = ''O'' THEN 1 ELSE 0 END) O, ' ||
' SUM(CASE WHEN tcis_person.reason_code = ''INF'' THEN 1 ELSE 0 END) INF, ' ||
' SUM(CASE WHEN tcis_person.reason_code = ''WFR'' THEN 1 ELSE 0 END) WFR ' ||
' FROM tcis_case ' ||
' join tcis_part_payment on tcis_case.tcis_case_id = tcis_part_payment.tcis_case_id ' ||
' join tcis_person on tcis_case.tcis_case_id = tcis_person.tcis_case_id ' ||
' where tcis_person.person_no = 1 ' ||
' AND tcis_case.unit = ''M'' ' ||
' AND tcis_case.sub_unit = ''P'' ' ||
' AND tcis_case.status IN (''P'', ''C'') ' ||
' AND (''' || P_DATE_FROM ||''' IS NULL OR tcis_case.offence_datetime >= TO_DATE(TO_CHAR(''' || P_DATE_FROM ||''', ''yyyy''),''yyyy'')) ' ||
' AND (''' || P_DATE_TO ||''' IS NULL OR tcis_case.offence_datetime < TO_DATE(TO_CHAR(''' || P_DATE_TO ||''', ''YYYY''),''yyyy'')) ' ||
' GROUP BY to_char(tcis_case.offence_datetime, ''yyyy''), tcis_case.status ';
END If;
DBMS_OUTPUT.put_line (c_stmt_str);
OPEN cur_getByPeriodFilter FOR c_stmt_str;
LOOP
FETCH cur_getByPeriodFilter INTO
CUR_ROW.YEAR,
CUR_ROW.STATUS,
CUR_ROW.TICKET_COUNT,
CUR_ROW.ORIGINAL,
CUR_ROW.PAID,
CUR_ROW.A,
CUR_ROW.D,
CUR_ROW.F,
CUR_ROW.N,
CUR_ROW.O,
CUR_ROW.INF,
CUR_ROW.WFR;
EXIT WHEN cur_getByPeriodFilter%NOTFOUND;
PIPE ROW(CUR_ROW);
END LOOP;
CLOSE cur_getByPeriodFilter;
END;
来自 sqlplus 的命令是:
select pkg_name.function('DD-MMM-YY','DD-MMM-YY', 1) from dual;
这看起来像是您作为字符串组成的较大“代码”的摘录。 这通常在您正在处理一些动态的东西时完成,例如将表或列名作为过程的参数传递。 你这样做的理由是什么? 也许我没有注意到它,但是 - 我在这里看不到任何动态。
如果是这样,您可以跳过所有这些单引号并编写一个不错的“正常” SELECT
语句,没有 escaping任何东西。
但是,如果您坚持使用它(或者确实有我没有看到的原因),请考虑使用 q-quoting 机制。 这就是您现在正在做的事情(使用无数的单引号;难以阅读、遵循和调试):
SQL> create or replace function f_test (par in varchar2)
2 return varchar2
3 is
4 l_str varchar2(200);
5 l_res varchar2(1);
6 begin
7 l_str := 'select ''A'' first_column from dual';
8 execute immediate l_str into l_res;
9 return l_res;
10 end;
11 /
Function created.
SQL> select f_test(null) from dual;
F_TEST(NULL)
-------------------------------------------------------------
A
SQL>
或者,您可以这样做:
SQL> create or replace function f_test (par in varchar2)
2 return varchar2
3 is
4 l_str varchar2(200);
5 l_res varchar2(1);
6 begin
7 l_str := q'[select 'A' first_column from dual]'; --> this
8 execute immediate l_str into l_res;
9 return l_res;
10 end;
11 /
Function created.
SQL> select f_test(null) from dual;
F_TEST(NULL)
------------------------------------------------------------
A
SQL>
所以:
q
关键字,就如此容易。
您发布的评论说没有发生任何事情(除了现在的代码可能更简单一些)。
我没有你的数据,所以我不能自己测试——你必须自己找到罪魁祸首,代码太多了。
你得到的错误说(例如)这个:
SQL> select to_number('A') from dual;
select to_number('A') from dual
*
ERROR at line 1:
ORA-01722: invalid number
Oracle 告诉你错误的 position - 你能从中找到什么吗?
转义字符中的错误会导致 cursor 的OPEN
出现问题
ORA-01722: invalid number error
是运行时错误,您无法将字符串转换为FETCH
中的数字。
您可以尝试通过反复试验找出它是哪一列(用null
逐步替换每个数字列),但很可能是AMT_ORIGINAL
或AMT_PAID
。
如果列是VARCHAR2
并且您看到诸如100,50
或100.50
之类的值,则问题是您的NLS_NUMERIC_CHARACTERS
不匹配。
您必须使用正确的设置将字符串显式转换为数字。 例如100.50
sum(to_number(AMT_PAID, '9999999D99', 'NLS_NUMERIC_CHARACTERS=''.,'''))
检查此答案以获取更多详细信息。
附加评论
如果传入DATE
参数,则不需要双重转换TO_DATE(TO_CHAR(
使用简单的参数
如果您要使用推荐的绑定变量,则不需要动态 cursor。 请参阅下面的简化实现
您可能想要一个动态的 cursor 来获得有效的执行计划,以便在有和没有日期范围的情况下进行搜索 - 请参阅此处的详细信息
简化实施
CREATE or replace FUNCTION getReportData(
P_DATE_FROM IN DATE,
P_DATE_TO IN DATE)
RETURN RPTTCI1328_TABLE PIPELINED IS
CURSOR cur_getByPeriodFilter
IS
SELECT to_char(offence_datetime,'YYYY') year,
/* process mumeric strings with decimal point e.g. 100.50 */
sum(to_number(AMT_PAID, '9999999D99', 'NLS_NUMERIC_CHARACTERS=''.,'''))
from tab
where tab.offence_datetime >= P_DATE_FROM and tab.offence_datetime < P_DATE_TO
group by to_char(offence_datetime,'YYYY')
;
CUR_ROW RPTTCI1328_ROW := RPTTCI1328_ROW(null,null);
BEGIN
OPEN cur_getByPeriodFilter;
LOOP
FETCH cur_getByPeriodFilter INTO
CUR_ROW.YEAR,
CUR_ROW.PAID;
EXIT WHEN cur_getByPeriodFilter%NOTFOUND;
PIPE ROW(CUR_ROW);
END LOOP;
END;
/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.