oracle - 执行包含更新和插入语句的存储过程时出现问题

标签 oracle stored-procedures plsql

我是 PLSQL 新手,我正在尝试执行此处显示的存储过程。

此存储过程将检查特定行并根据计数更新表或插入。但我整体上遇到了以下错误。

31/18 PL/SQL: ORA-00928: missing SELECT keyword
31/1 PL/SQL: SQL Statement ignored
37/26 PL/SQL: ORA-00933: SQL command not properly ended
36/1 PL/SQL: SQL Statement ignored

我已尽力解决这些问题。您能帮忙解决这个问题吗?

这是我为此任务编写的过程:

CREATE OR REPLACE PROCEDURE LPR_LP_TEST.SP_PTMS_NOTES
(
p_app_lse_s     IN mjl.app_lse_s%TYPE,
p_dt_ent_s      IN mjl.dt_ent_s%TYPE,
p_note_type_s   IN mjl.note_type_s%TYPE,
p_prcs_c        IN mjl.prcs_c%TYPE,
p_prio_c        IN mjl.prio_c%TYPE,
p_note_title_s  IN mjl.note_title_s%TYPE,
p_info1_s       IN mjl.info1_s%TYPE,
p_info2_s       IN mjl.info2_s%TYPE
)
AS
v_rowcount_i     number;
v_lien_date    mjl.info1_s%TYPE;
--v_lien_date      NMAC_PTMS_NOTEBK_SG.LIEN_DT%TYPE;
v_asst_amount    mjl.info2_s%TYPE;

BEGIN
app_lse_s:=trim(app_lse_s);
dbms_output.put_line(app_lse_s);

select LIEN_DT,ASES_PRT_1_AM 
INTO v_lien_date,v_asst_amount
from NMAC_PTMS_NOTEBK_SG
where LSE_ID ='&2';

select count(*) into v_rowcount_i from MJL where trim(app_lse_s) ='&2';

if v_rowcount_i = 0 then
begin
Insert into MJL
('app_lse_s','dt_ent_s','note_type_s','prcs_c','prio_c','note_title_s','info
1_s','Info2_s') 
values ('&2','sysdate','SPPT','Y','1','Property Tax 
Assessment','v_lien_date','v_asst_amount');
end;
else
begin
update mjl
set dt_ent_s = 'sysdate' and note_type_s = 'SPPT' and prcs_c = 'Y' and 
prio_c = '1' and note_title_s = 'Property Tax Assessment' and info1_s = 
'v_lien_date' and Info2_s = 'v_asst_amount'
where trim(app_lse_s) = '&2';
end;
end if;
commit;
end;
/

最佳答案

我相信你的程序应该类似于:

CREATE OR REPLACE PROCEDURE LPR_LP_TEST.SP_PTMS_NOTES
(
p_app_lse_s     IN mjl.app_lse_s%TYPE,
p_dt_ent_s      IN mjl.dt_ent_s%TYPE,
p_note_type_s   IN mjl.note_type_s%TYPE,
p_prcs_c        IN mjl.prcs_c%TYPE,
p_prio_c        IN mjl.prio_c%TYPE,
p_note_title_s  IN mjl.note_title_s%TYPE,
p_info1_s       IN mjl.info1_s%TYPE,
p_info2_s       IN mjl.info2_s%TYPE
)
AS
  v_rowcount_i   number;
  v_lien_date    mjl.info1_s%TYPE;
  --v_lien_date    NMAC_PTMS_NOTEBK_SG.LIEN_DT%TYPE;
  v_asst_amount  mjl.info2_s%TYPE;
  v_app_lse_s    mjl.app_lse_s%TYPE;
BEGIN
  v_app_lse_s := trim(p_app_lse_s);

  -- I hope this dbms_output line is for temporary debug purposes only
  -- and will be removed in the production version!
  dbms_output.put_line(app_lse_s);

  merge into mjl tgt
    using (select lse_s app_lse_s,
                  sysdate dt_ent_s,
                  'SPPT' note_type_s,
                  'Y' prcs_c,
                  '1' prio_c,
                  'Property Tax Assessment' note_title_s,
                  lien_dt info1_s,
                  ases_prt_1_am info2_s
           from   nmac_ptms_notebk_sg
           where  lse_id = v_app_lse_s) src
      on (tgt.app_lse_s = src.app_lse_s)
  when matched then
    update set tgt.dt_ent_s = src.dt_ent_s,
               tgt.note_title_s = src.note_title_s,
               tgt.info1_s = src.info1_s,
               tgt.info2_s = src.info2_s
    where tgt.dt_end_s != src.dt_ent_s
    or    tgt.note_title_s != src.note_title_s
    or    tgt.info1_s != src.info1_s
    or    tgt.info2_s != src.info2_s
  when not matched then
    insert (tgt.app_lse_s,
            tgt.dt_ent_s,
            tgt.note_type_s,
            tgt.prcs_c,
            tgt.prio_c,
            tgt.note_title_s,
            tgt.info1_s,
            tgt.info2_s)
    values (src.app_lse_s,
            src.dt_ent_s,
            src.note_type_s,
            src.prcs_c,
            src.prio_c,
            src.note_title_s,
            src.info1_s,
            src.info2_s);

  commit;
end;
/

关于您的程序以及我为提出上述程序所做的事情需要注意的事项:

  1. 您倾向于将所有内容都用单引号引起来。单引号用于将某些内容声明为字符串,即 some_variable := 'string value' 。如果您在实际上是标识符的内容周围放置单引号,那么您实际上是在告诉 Oracle 将其视为字符串 - 这将导致各种错误!唯一应该在标识符周围使用引号的情况是当标识符的名称区分大小写时,并且应该使用双引号。例如。 select * from "lower_case_tablename" 。 (注意:我在这里说“应该”,但这是一个指导原则;您可以在不区分大小写的标识符名称周围使用双引号,但如果这样做,名称应该为大写 - 即 select * from "DUAL"; )。

  2. 您的更新语句语法不正确 - 对单个语句中多个列的更新用逗号分隔,而不是 and

  3. beginends围绕插入和更新语句是不必要的。

  4. 如果您要使用隐式游标(即过程主体中的 select ... into <variable list> from ...),您需要确保处理可能引发的 NO_DATA_FOUND 和 TOO_MANY_ROWS 异常。

  5. 我设置了一个变量来存储参数 p_app_lse_s 传入的修剪值 - 我认为这就是您想要做的?我还用变量替换了对“&2”的所有调用。

  6. 如果您需要执行更新插入(即,如果该行尚不存在则插入,否则更新),然后考虑 MERGE 语句。如果您绝对必须将它们分开,那么首先不要检查该行是否存在;首先执行插入并检查 DUP_VAL_ON_INDEX 错误 - 然后在异常处理程序中执行更新。或者,首先执行更新并检查 SQL%ROWCOUNT 以查看行是否已修改,如果没有,则执行插入。不过,MERGE 更好,因为这意味着某人没有机会在数据库在两个语句之间切换的瞬间在不同的 session 中插入行。

  7. 通过使用 MERGE 语句,我能够将所有逻辑合并到单个 SQL 语句中,这使您的过程更简单且更易于调试。首先,我打赌您的程序中的其他参数需要在程序内部使用;很容易更新合并语句中的源查询,用参数名称替换硬编码值!我将把它作为一个练习留给你来做。

  8. 如果您从 nmac_ptms_notebk_sg 获取 info1_s 和 info2_s 值,您真的需要 p_info1_s 和 p_info2_s 参数吗?恕我直言,他们似乎不需要。

最后,此过程一次只执行一个 app_lse_s 的工作。如果您的数据库处理是 OLTP,那就没问题。如果它正在进行批处理,并且您的代码类似于以下伪代码:

for each row in <this cursor>
loop
  execute the lpr_lp_test.sp_ptms_notes procedure
end loop

那么您最好将 sp_ptms_notes 过程合并到调用过程中,并在单个 MERGE 语句中完成工作。


预计到达时间:如果您有一个临时表(可以是外部表或全局临时表 (GTT),甚至是普通堆表),其中包含要加载到数据库中的数据,那么您的合并语句将变成这样:

  merge into mjl tgt
    using (select trim(st.app_lse_s) app_lse_s,
                  sysdate dt_ent_s,
                  'SPPT' note_type_s,
                  'Y' prcs_c,
                  '1' prio_c,
                  'Property Tax Assessment' note_title_s,
                  npns.lien_dt info1_s,
                  npns.ases_prt_1_am info2_s
           from   staging_table st
                  inner join nmac_ptms_notebk_sg npns-- maybe left outer join?
                    on trim(st.app_lse_s) = npns.lse_s) src
      on (tgt.app_lse_s = src.app_lse_s)
  when matched then
    update set tgt.dt_ent_s = src.dt_ent_s,
               tgt.note_title_s = src.note_title_s,
               tgt.info1_s = src.info1_s,
               tgt.info2_s = src.info2_s
    where tgt.dt_end_s != src.dt_ent_s
    or    tgt.note_title_s != src.note_title_s
    or    tgt.info1_s != src.info1_s
    or    tgt.info2_s != src.info2_s
  when not matched then
    insert (tgt.app_lse_s,
            tgt.dt_ent_s,
            tgt.note_type_s,
            tgt.prcs_c,
            tgt.prio_c,
            tgt.note_title_s,
            tgt.info1_s,
            tgt.info2_s)
    values (src.app_lse_s,
            src.dt_ent_s,
            src.note_type_s,
            src.prcs_c,
            src.prio_c,
            src.note_title_s,
            src.info1_s,
            src.info2_s);

您可以看到我已将 nmac_ptms_notebk_sg 表加入到临时表中,并使用它来生成需要合并到 mjl 表中的数据集。如果您的文件/临时表还包含其他列(dt_ent_s、note_type_s 等)的信息,那么您可以将硬编码值替换为临时表中的列。

关于oracle - 执行包含更新和插入语句的存储过程时出现问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43984876/

相关文章:

MySQL 存储过程变量格式在 SELECT 语句中不起作用

sql - 如何用 • 替换单词但仅显示第一个和最后一个字符

oracle - APEX_WEB_SERVICE.MAKE_REST_REQUEST 结果为 ORA-29248 : an unrecognized WRL was used to open a wallet

oracle - 监听器拒绝连接,并出现以下错误 : ORA-12505, TNS:监听器当前不知道连接描述符中给出的 SID

oracle - 查询导致数据库失败,ORA-0113

php - 存储过程、MySQL 和 PHP

sql - 从 Oracle SQL 过程中的表执行 SQL 语句

oracle - oracle 过程中的某些 ID 不显示数据

sql - 简单的 WHERE 子句,但保留提取的行并填充它们将为空值

sql - 从存储过程中调用存储过程的性能问题