哪位PL/SQL高手帮帮忙,第一次用PL/SQL写存储过程遇到问题不知道怎么解决,多多指教! (200分)

  • 主题发起人 主题发起人 GipsyCN
  • 开始时间 开始时间
G

GipsyCN

Unregistered / Unconfirmed
GUEST, unregistred user!
请问下面的过程有没有语法错误?怎么编译不通过?

CREATE OR REPLACE PROCEDURE SP_AGGR_FEE
( AGGR_RESULT IN OUT PG_AGGR_DATASET.C_AGGR_DATASET,
DATE_FROM IN DATE,
DATE_TO IN DATE,
CustParam IN VARCHAR2,
FEE_RATE0 IN NUMBER,
FEE_RATE1 IN NUMBER,
FEE_RATE2 IN NUMBER,
FEE_RATE3 IN NUMBER,
FEE_RATE4 IN NUMBER,
REC_CNT OUT NUMBER,
TOTAL_FEE OUT NUMBER
) IS
CURSOR C_REGISTER IS
SELECT DISTINCT A_CarInfo.GSMVoiceNum,A_CarInfo.TruckId,A_Register.C_Name,A_Register.ContractNo,A_Register.BankName,
A_Register.BankNo,A_Register.Accounts,A_Register.ServiceCharge from A_Register,A_DriverInfo,A_CarInfo
WHERE A_CarInfo.DriverId=A_DriverInfo.DriverId AND
A_DriverInfo.TheName=A_Register.C_Name AND
A_DriverInfo.RegisterID=A_Register.RegisterID;
C_REGISTER_Rec C_REGISTER%ROWTYPE;
sSQL VARCHAR2(255);
Temstr VARCHAR2(255);
Temstr1 VARCHAR2(255);
Temstr2 VARCHAR2(255);
sRxTxSQL CONSTANT VARCHAR2(255) :='SELECT C_Name, ContractNo, GSMVoiceNum, TruckID, Bankname, ServiceCharge, Accounts FROM A_RxTx WHERE ';
Mysqlstr0 CONSTANT VARCHAR2(255) :='(CommandType = ''t'') AND (Describe LIKE ''发送点播信息%'') AND (Sender = ''通信中心'')AND (SendSource NOT LIKE ''%WebGIS服务中心%'')';
Mysqlstr1 CONSTANT VARCHAR2(255) :='(CommandType=''t'') and (Describe LIKE ''发送点播信息%'') and (Sender=''通信中心'') and (SendSource LIKE ''%WebGIS服务中心%'')';
Mysqlstr2 CONSTANT VARCHAR2(255) :='(CommandType = ''D'') AND (Describe LIKE ''询问位置%'')and(Sender = ''通信中心'') and (SendSource LIKE ''%WebGIS服务中心%'')';
Mysqlstr3 CONSTANT VARCHAR2(255) :='(CommandType = ''M'' OR CommandType = ''m'')AND(Receiver = ''通信中心'') AND (Describe LIKE ''%用户直接手机接入%'')';
Mysqlstr4 CONSTANT VARCHAR2(255) :='(Sender = ''通信中心'') AND (SendSource LIKE ''%用户手机%'')';
nmSum NUMBER(8,2);
type tyQryCur IS REF CURSOR;
curSubDataSet tyQryCur;
curSubDataSet_Rec curSubDataSet%ROWTYPE;
i INT;
FUNCTION GetHex(aStr IN CHAR)
RETURN NUMBER IS
BEGIN
IF (aStr = '1') or (aStr = '2') or (aStr = '3') or (aStr = '4') or
(aStr = '5') or (aStr = '6') or (aStr = '7') or (aStr = '8') or (aStr = '9') THEN
RETURN TO_NUMBER(aStr);
ELSIF (aStr = 'b') or (aStr = 'B') THEN
RETURN 11;
ELSIF (aStr = 'c') or (aStr = 'C') THEN
RETURN 12;
ELSIF (aStr = 'd') or (aStr = 'D') THEN
RETURN 13;
ELSIF (aStr = 'e') or (aStr = 'E') THEN
RETURN 14;
ELSIF (aStr = 'f') or (aStr = 'F') THEN
RETURN 15;
END IF;
END GetHex;
BEGIN
OPEN C_REGISTER;
REC_CNT := C_REGISTER%ROWCOUNT;
IF REC_CNT = 0 THEN
CLOSE C_REGISTER;
RETURN;
END IF;
TOTAL_FEE := 0;
LOOP
nmSum := 0;
FETCH C_REGISTER INTO C_REGISTER_Rec;
EXIT WHEN C_REGISTER%NOTFOUND;
Temstr1 := 'and Receiver='||C_REGISTER_Rec.GSMVoiceNum;
Temstr2 := ' AND (Content LIKE ''%*M*'||C_REGISTER_Rec.TruckID||'%)';
FOR i IN 0..4 LOOP
IF i = 0 THEN
sSQL := sRxTxSQL||Mysqlstr0||Temstr1;
ELSIF i = 1 THEN
sSQL := sRxTxSQL||Mysqlstr1||Temstr1;
ELSIF i = 2 THEN
sSQL := sRxTxSQL||Mysqlstr2||Temstr1;
ELSIF i = 3 THEN
sSQL := sRxTxSQL||Mysqlstr3||Temstr1;
ELSIF i = 4 THEN
sSQL := sRxTxSQL||Mysqlstr4||Temstr2;
END IF;
sSQL := sSQL||'and (SendTime >'||DATE_FROM||' and SendTime <'||DATE_TO||')';
OPEN curSubDataSet FOR sSQL;
IF i = 0 THEN
nmSum := nmSum + curSubDataSet%ROWCOUNT*FEE_RATE0;
ELSIF i = 1 THEN
nmSum := nmSum + curSubDataSet%ROWCOUNT*FEE_RATE1;
ELSIF i = 2 THEN
IF curSubDataSet%ROWCOUNT <> 0 THEN
LOOP
FETCH curSubDataSet INTO curSubDataSet_Rec;
EXIT WHEN curSubDataSet%NOTFOUND;
IF LENGTH(curSubDataSet_Rec.Content) = 9 THEN
Temstr := Copy(Content,3,2);
nmSum := nmSum + GetHex(Copy(Temstr,1,1))*16 + GetHex(Copy(Temstr,2,1));
END IF;
END LOOP;
END IF;
ELSIF i = 3 THEN
nmSum := nmSum + curSubDataSet%ROWCOUNT*FEE_RATE2;
ELSIF i = 4 THEN
nmSum := nmSum + curSubDataSet%ROWCOUNT*FEE_RATE3;
END IF;
CLOSE curSubDataSet;
nmSum := nmSum + C_REGISTER_Rec.ServiceCharge;
TOTAL_FEE := TOTAL_FEE + nmSum;
WHILE LENGTH(C_REGISTER_Rec.Accounts) < 21 LOOP
C_REGISTER_Rec.Accounts := C_REGISTER_Rec.Accounts||'0';
END LOOP;
Temstr := C_REGISTER_Rec.ContractNo||C_REGISTER_Rec.BankNo||C_REGISTER_Rec.Accounts;
INSERT INTO T_AGGR_FEE(CONTENT,FEE)
VALUES (Temstr,nmSum);
INSERT INTO A_ChargingTable(TruckId,C_Name,ContractNo,BankName,BankNo,Accounts,ServiceCharge,Money,ChargeDate,batch)
VALUES (C_REGISTER_Rec.TruckId,C_REGISTER_Rec.UserName,C_REGISTER_Rec.ContractNo,C_REGISTER_Rec.BankName,
C_REGISTER_Rec.BankNo,C_REGISTER_Rec.Accounts,C_REGISTER_Rec.ServiceCharge,
C_REGISTER_Rec.Sum,SYSDATE,CustParam);
ENDLOOP;--FOR i IN 0..4 LOOP
ENDLOOP;--Lookup Curssor C_REGISTER
CLOSE C_REGISTER;
END SP_AGGR_FEE;
 
过程有点长,希望哪位PL/SQL高手耐心看下去,并在语法和变量使用上并提出一些建议,

不胜感谢!分不够我会在加。
 
kao,谁有空看完啊,你不会直接show error,把错误信息放进来吗?
 
最好用小写,存储过程一般不用太长(就说这么多)
 
最好有异常机制,成功则Commit,否则RollBack;
 
感谢两位的参与!
过程是比较长。不过使用过PL/SQL Developer的大侠也都知道,错误提示纯扯,没用!
 
kao,谁有空看完啊,你不会直接show error,把错误信息放进来吗?
 
我以前是用MSSQL的。现在用Oracle 9i,和MSSQL相比,在操作上Oracle太差了。
我们的系统转移到Oracle上后速度奇慢无比。Oracle 9i运行时也特别耗资源,
我算时领教了。据说Oracle9i是用纯Java开发的,各位大侠说是不是?
难怪速度那么慢。
 
把错误信息贴上来吧?(用SQL Plus)
建表调试很麻烦!
 
感谢各位!
在PL/SQL Developer中的错误提示是:
PLS-00103:出现符号“SP_AGGR_FEE”在需要下列之一时:Loop
 
另外:在SQL*PLUS创建过程时的错误是:
警告: 创建的过程带有编译错误。
 
顺便推荐一篇文章,转载的,感觉写得还不错,希望对大家有帮助:

Oracle--毛病篇
蓝森林 http://www.lslnet.com 2001年12月5日 10:03

作 者: 小赵
(slimzhao@21cn.com)

前言:

本文纯为牢骚, 怨言, 一为笔者对ORACLE软件如此这般的疑惑与不满,

二来其实可以总结ORACLE中的一些不规则性, 为ORACLE学习者提供一些借鉴.

三来看起当时的心情颇为抓狂, 写下来, 算是对一些不良心态的反思.

有一些是笔者早期初涉ORACLE时的一些误解, 冤枉它了. 在后记中都有正名.

1. 改变当前用户时SQLPLUS环境变量的设置是否被继承到新的用户环境下
  混乱, serverout 没有被继承, 而pagesize被继承.

2. SQLPLUS环境下命令行编辑功能微弱得可怜, 看看readline, 看看MySQL

3. SQLPLUS环境的命令行没有历史记录功能, 看看MySQL. gdb, bash

4. 命名规范混乱得一踏糊涂. user_tab_columns, user_con_constraints
  user_tables, usre_cons_columns

5. SQLPLUS环境的初始化文件的location的要求, 看看.bashrc, .vimrc, .netrc
  .emacs, .newsrc...摸一个都比它强, 我要使用你的login.sql还非得在每
  一个当前目录下都弄一份, 随身携带呀? 好在这一点可以通过SQLPATH环境
  变量的设置来搞定.

6. 注释格式:--, 什么玩意呀, HTML用, perl用#, php用#或/**/或
  //, C用/**/, C++用/**/和//, lisp用;;, 汇编用;, QBASIC用rem 和',
  SHELL,SED,AWK用#, 你干吗要自创一种. 自以为很鸟啊, 与别人兼容一点会
  小你什么身份.

7. 看看它的PL/SQL, 号称第N代的高级语言, function因为必需返回一个值而
  存在, 除此之外我看不出它与其它的procedure有什么区别. 又是trigger,
  又是package, 又是procedure, 又是function, 搞什么东东呀. 别人是把
  复杂的东西简单化, 我看ORACLE是要把简单的东西复杂化, 同样是PL/SQL
  程序, dbms_output.put_line被设计成在function里单独调用时不能输出.
  非要一个procedure单独调用它时它才会输出.

8. 单引号问题, Javascript可以兼容使用单双引号, perl可以, php也可以, 它
  们可以是因为它们想兼容, 想对用户友好, C语言不允许, 那是因为它严谨.
  人家留着有大用场. 你ORACLE不允许使用双引号又是什么原由, 说来听听.

9. 别人的用户界面设计是"没有消息就是最好的消息", 或是"无论发生什么, 都
  要让用户知道", 看看它在sqlplus下的变量:
  var i number;
  i:=5;
  print i;
  你什么看见什么, 什么都没有! 你又能从这知道什么, 好消息?
  PS:我至今都不知道这说明了什么? i的值为NULL?, i不能在这下面赋值?
  不能赋值你说呀, 你不说我怎么知道不能赋值, 虽然你很能有意见地显示了
  一个空行, 但是你还是要说呀, 你真的不能赋值吗? 你不是真的不能赋值吧?
  ...
  后记: 好在我今天知道了要用
  SQL> exec :i := 5;
  SQL> print :i
  但总不能让我掘地三尺才找得出来这些浅显的东西吧.

10. 在sqlplus下help set看看, 找出一个叫set severout[put] on|off的选项
  注意, 它是severout, 你在sqlplus下set severout on试试,
  SP2-0158: unknown SET option "severout" 了吧?
  set serverout on
  是的, 就一个字母而已.
  编程大师说: 任何软件都有BUG.
  但, 一个如此小的BUG可以让一个全球第二大的软件公司带着它走过几个版本?
  Oracle8.15 Oracle8.16 Oracle8.17...难道保留错误是为了兼容性?
  后记: 我终于看到Oracle9i版里对此有了改进, 略感欣慰.

11. PL/SQL的用户们, 看看下面:

  创建一个没带参数的procedure:
  create or replace procedure proc_name as
  i number;
  begin
  ...
  end proc_name;

  创建一个带参数的procedure:
  create or replace procedure proc_name(arg1,...) as
  i number;
  begin
  ...
  end proc_name;
  你很习惯用declare来声明吗? 不行, 这里不行, 你必需听我的, 用as

  创建一个触发器:
  create or replace trigger tri_name before insert on tname for each
  row
  declare
  i number;
  begin
  ..
  end;
  因为这是触发器, 所以它要用declare来声明变量, 尽管触发器用的也是PL/
  SQL的语法. 但我们是为了区别于其它类型的存储过程, 为了让用户觉得
  ORACLE高深莫测一点.
  怎么就一个end;不是end tri_name吗? 是的, procedure和function是这样
  设计的, 但这样可以让用户觉得更难用一点么!
  
  (sqlplus下)你调用一个不带参数的procedure:
  call proc_name();
  (sqlplus下)你调用一个不带参数的procedure:
  call proc_name(arg1, arg2);
  是的, 它不是可选的, 它必需如此, 定义一个没有参数的procedure就是不
  要空的()号, 但调用它的时侯就是要, 定义一个有参数的procedure当然也要.
  function的情况与这个也一样.

  你调用一个function:
  ret_val := func_name();
  什么? 你不需要ret_val, 不行, 这是强买强卖, 不要不行. 否则我不干活!
  而且, 哼哼, 我给出的错误信息保证你看不懂.

  你要玩更高级的设计方法, 用上包了:
  create or replace package pack_name as
  ....
  end pack_name;
  是的, 这只是包的声明.
  要想定义这个包, 你还得:
  create or replace package body pack_name as
   procedure proc_name is
   --你不是说用as吗? 是的, 但是这里as已经被上一条语句用了, 呶! 你
   也看到了. 用is有什么不好?
   end proc_name
   function func_name is
   ...
   end func_name
  end pack_name;

12. 看看出错信息, 来自SQLPLUS的, 来自Pro*C预编译器的, 来自其它工具如
  imp, exp, sqllda的. 说不准你也别误导呀.

13. 疲惫不堪的Pro*C程序员们:
  在函数foo中你已经声明了
  EXEC SQL WHENEVER SQLERROR GOTO sql_err;
  在另一个函数bar里你只想默默地使用
  EXEC SQL WHENEVER SQLERROR CONTINUE;
  但你没特别声明, Pro*C的预编译器会告诉你, 它在函数bar中找不到sql_err
  标号!!! 它可以跨函数使用标号. 它也敢自称是一个预处理器跟编译器沾沾
  边, 刚才用陈桥五笔敲出来的处理器三个字恰好跟处理品是同样的键码, 我看
  叫处理品倒是名符其实.

14. DDL语句的用户们:
  alter table tname add(col1 type...);
  alter table tname modify(col1 type...);
  alter table tname drop column(col1);
  为何非要在drop里多出来一个column?

15. 命名
  user_cons_columns;
  user_constraints;
  user_rollback_segs;
  它真的要把segment统统缩写为segs吗? 把constraints缩写为cons? 把
  columns缩写为cols? 不, ORACLE的程序员们想怎么样就怎么样. 他们是觉得
  单词的全称太长了吗? 不见得, desc dba_users;看一下
  看看INITIAL_RSRC_CONSUMER_GROUP字段, 看看temporary_tablespace字段.

  再来看看一下create table:
  create table tname (col1 type,...) storage(initial .. next ..
  minextents .. maxextents ..) ...;
  desc user_tables;
  inital_extent
  next_extent
  minextents
  maxextents
  不要问他们为什么有的用单数有的用复数, 因为他们是ORACLE. 他们想这样.
  dba_tables中owner
  dba_sequences中叫sequence_owner
  user_tab_columns是关于一张表中列信息的, _tab_columns
  user_cons_columns是关于表中字段的约束信息的, _cons_columns

后话: 牢骚发完了, 继续得埋头搞开发, 写代码, 毕竟, 人类在一个fortran语言大行其道的年代就已经登上了月球, 所以, 争论语言或工具的优劣强弱还不如把现有的功能充分高效地利用起来. 如此一说, 算是什么?
 
其中参数AGGR_RESULT IN OUT PG_AGGR_DATASET.C_AGGR_DATASET是要返回一个数据集。
按照大富翁上大侠说的方法先这样做了:
--建临时表
CREATE TABLE T_AGGR_FEE(CONTENT Varchar2(500), FEE NUMBER(11,2));
--建包,供过程存放数据集用
CREATE OR REPLACE PACKAGE PG_AGGR_DATASET
AS
TYPE C_AGGR_DATASET IS REF CURSOR RETURN T_AGGR_FEE%ROWTYPE;
END;

跟大家多学习。
 
在SQLPLUS里编译,不通过的话可以用SHOW ERRORS;命令来看看是什么错误

对于这么长的东西不好一次找出错误来,应该先检查其中一部分,没问题再
增加一些,这样一步步的找出问题来
 
感谢 沙隆巴斯的主人 的提醒!
在PL/SQL Developer中 我试过把过程体begin end之间的东西去掉,看看是不是变量声名有问题,结果提示一样;
然后我试试把变量声名一个一个去掉,结果还是那样。
SQL/PLUS的变量是不是和VB中的一样,不声名也可以用?
 
问题是我不知道怎么调试。SQL编程语言一般都比较难调试,
在MSSQL的SQL Analyser中调试T—SQL时也吃过苦头,毕竟也总结了一些经验,
现在用起来感觉还是很好的,比较复杂的存储过程也搞过;
对PL/SQL不熟悉,感觉是狗咬刺猬无处下口。
各位多指教了!
 
分步骤来,先把最简单的调通了,再一点点增加内容,这叫排除查错法
 
再提。
劳驾各位看看语法匹配上有没有什么不对的地方,看程序不一定要参悟全部内容。
各位大侠说是吧?
另外:错误提示条停在过程的最后一句。
还有请教各位:在PL/SQL中End If 和EndIf(中间没有空格)有什么区别?

 
感谢大侠 沙隆巴斯的主人!
 
后退
顶部