繁体   English   中英

select 语句中的 Escaping 字符以及 Z30162ED78B6C10F731411F2FC440C2 中的 cursor 中的字符

[英]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_ORIGINALAMT_PAID

如果列是VARCHAR2并且您看到诸如100,50100.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM