【问题标题】:Issue while executing stored procedure which consists both update and insert statements执行包含更新和插入语句的存储过程时出现问题
【发布时间】:2017-05-15 16:48:39
【问题描述】:

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

此存储过程将检查特定行并根据计数更新表或插入。但我总体上低于错误。

31/18 PL/SQL:ORA-00928:缺少 SELECT 关键字
31/1 PL/SQL:忽略 SQL 语句
37/26 PL/SQL:ORA-00933:SQL 命令未正确结束
36/1 PL/SQL:忽略 SQL 语句

我尽力解决它们。能帮忙解决一下问题吗?

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

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;
/

【问题讨论】:

  • 你一直引用'&2',但这是一个过程。如果您希望将 &2 替换为一个值,则应该使用一个参数。另外,当您没有声明该名称的变量时,app_lse_s:=trim(app_lse_s); 是什么?此外,您不需要围绕更新和插入的额外开始/结束 - 这可能应该是单个 MERGE 语句!最后,您将插入语句中的列名用单引号括起来;或者不应该有引号(列不区分大小写)或双引号(列区分大小写)。
  • 感谢@Boneist 的宝贵建议。 "&2" 我在执行程序时输入值。现在我在更新语句中收到以下错误。 “错误第 35 行,第 26 列,第 35 行,第 35 行,第 28 行,找到‘和’,期望:; - 或 - 日志 - 或 - 返回 - 或 - 位置 - 或 - , - 或 - + - || - 或 - * / MOD REM -or- ** -or- ( (+) AT DAY MULTISET YEAR" 。我不确定我在哪里失踪。你能帮我解决这个问题吗?
  • 请参阅下面我的回答中的第 2 点。至于"&2" i am entering the value while executing the procedure - 您可以通过将值作为参数传递来做到这一点,例如begin lpr_lp_test.sp_ptms_notes(p_app_lse_s => '123', ....); end; /

标签: oracle stored-procedures plsql


【解决方案1】:

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

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. 您的更新语句语法不正确 - 对单个语句中多个列的更新用逗号分隔,而不是 ands。

  3. 插入和更新语句周围的begins 和ends 是不必要的。

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

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

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

  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 语句中完成工作。


ETA:如果您有一个包含要加载到数据库中的数据的暂存表(可以是外部表或全局临时表 (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 等)的信息,那么您可以用暂存表中的列替换硬编码值。

【讨论】:

  • 我正在从一个文本文件中获取一堆 lse_id,并尝试将它们作为循环中的 shell 脚本中的参数传递给存储过程。如何处理?
  • 如果文件对数据库可见(即数据库进程所有者),那么您可以使用外部表将文件公开为数据库中的表。否则就有 SQL*Loader,或者你现在拥有的逐行处理。就个人而言,我会将文件中的数据放入临时表中,然后运行单个合并语句将数据加载到主表中。
  • 这是一个来自供应商的文本文件。通过 Shell 脚本,我仅捕获 lse_id 列并在脚本中运行循环以传递此参数以检查数据库以在 mjl 表中插入或更新。为此,我在 shell 脚本中内部调用了这个存储过程。我们可以那样做吗?
  • 是的,当然可以,尽管这可能不是最快的方法。我仍然会将 id 加载到临时表中,然后在单个合并语句中使用该表来更新 mjl 表。想一想:你有一个装满水的浴缸、一个勺子和一个水壶。哪种方式排空浴缸最快?你的方法相当于用勺子;我的相当于拔掉浴塞!
  • 当我执行存储过程时,我收到以下错误“ORA-00001:违反唯一约束 (LPR_LP_TEST.MJL_IDX0)”。在 MJL 表中,他们在 app_lse_s 和 dt_ent_s 上创建了唯一索引。我还需要在更新语句中使用 dt_ent_s 吗?当匹配然后更新设置 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
猜你喜欢
  • 1970-01-01
  • 2021-07-06
  • 2019-03-19
  • 1970-01-01
  • 2012-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多