【问题标题】:Quantity increase / decrease pl/sql数量增减pl/sql
【发布时间】:2016-01-15 00:41:57
【问题描述】:

我编写了这个程序,旨在根据订单详细信息中的 pno 和 ono 单独删除项目。删除的项目将相应地放入表 RECORDS 和 REMOVED_ODETAILS 中。一个订单多次购买的某些商品,以及每个商品的购买编号,即 QTY。我的问题是如何从 odetails 中减少多次订购商品的 QTY -1 并增加 QTY REMOVED_ODETAILS +1?

create or replace PROCEDURE ONE_BY_ONE( ORD_NUM IN NUMBER(5), PRO_NUM IN NUMBER(5), QUANTITY NUMBER(5))
AS
CURSOR CUR1
IS
SELECT * FROM ORDERS 
WHERE ONO = ORD_NUM;

CURSOR CUR2
IS
SELECT * FROM ODETAILS
WHERE PNO = PRO_NUM
AND ONO = P_ONO;

VAL1 CUR1%ROWTYPE;
VAL2 CUR2%ROWTYPE;

BEGIN

OPEN CUR1;
OPEN CUR2;

LOOP
FETCH CUR1 INTO VAL1;
FETCH CUR2 INTO VAL2;

EXIT WHEN CUR1%NOTFOUND;
EXIT WHEN CUR1%NOTFOUND;


IF VAL1.SHIPPED IS NOT NULL THEN
   IF PRO_NUM = VAL2.PNO AND ORD_NUM = VAL2.ONO THEN
       IF ( VAL2.QTY >  QUANTITY) THEN
    FOR R IN (SELECT ONO, PNO FROM REMOVED_ODETAILS) LOOP
  --CODE NOT CHECK TO INSERT OR UPDATE ON REMOVED_ODETAILS
    IF (R.ONO != ORD_NUM AND R.PNO != PRO_NUM) THEN
    INSERT
      INTO REMOVED_ODETAILS
        (
          ONO,
          PNO,
          QTY
        )
        VALUES
        (
          VAR_CUR2.ONO,
          VAR_CUR2.PNO,
          VAR_CUR2.QTY
        );
        ELSE 
      UPDATE REMOVED_ODETAILS SET QTY = QTY - QUANTITY WHERE ONO = ORD_NUM AND 
                                                            PNO =PRO_NUM;
        END IF;
    END LOOP;

      INSERT INTO RECORDS(ID_NUMBER, ONO,CNO,DATE_SEND,CONDITION)
      VALUES(Seq.NEXTVAL, VAL1.ONO,VAL1.CNO,SYSDATE,'SEND BACK');

     ELSIF (VAL2.QTY = QUANTITY ) THEN 
      INSERT INTO RECORDS(ID_NUMBER, ONO,CNO,DATE_SEND,CONDITION)
      VALUES(Seq.NEXTVAL, VAL1.ONO,VAL1.CNO,SYSDATE,'SEND BACK');
     INSERT INTO REMOVED_ODETAILS( ONO, PNO, QTY) 
             VALUES(VAR_CUR2.ONO,VAR_CUR2.PNO, VAR_CUR2.QTY);
      DELETE FROM ODETAILS WHERE ONO = ORD_NUM AND `enter code here`PNO = PRO_NUM;
   END IF;  


       DBMS_OUTPUT.PUT_LINE('Item will be send back ');

        DELETE FROM ORDERS 
        WHERE ONO = ORD_NUM and 
        NOT EXISTS (select 1 from ODETAILS where ONO = ORD_NUM );

ELSIF VAL1.SHIPPED IS NULL THEN
       DBMS_OUTPUT.PUT_LINE('DO NOTHING ');
END IF;

END LOOP;
CLOSE CUR1;
CLOSE CUR2;
END ONE_BY_ONE;

客户

 create table customers (
  cno      number(5) not null primary key,
  cname    varchar2(30),
  street   varchar2(30),
  zip      number(5) references zipcodes,
  phone    char(12));

员工

create table employees (
 eno      number(4) not null primary key, 
 ename    varchar2(30),
 zip      number(5) references zipcodes,
 hdate    date);

订单

create table orders (
  ono      number(5) not null primary key,
  cno      number(5) references customers,
  eno      number(4) references employees,
  received date,
  shipped  date);

详细信息

 create table odetails (
  ono      number(5) not null references orders,
  pno      number(5) not null references parts,
  qty      integer check(qty > 0),
  primary key (ono,pno));

记录

create table RECORDS
       id_number  number(5) not null primary key,
       ono        number(5) not null,
       cno        number(5) not null,
       date_send  date,
       condition  varchar22(30));

REMOVED_ODETAILS

create table REMOVED_ODETAILS ( 
      ono      number(5) not null,
      pno      number(5) not null,
      qty      integer check(qty > 0),
      primary key (ono,pno));

之前

订单

    ONO         CNO         ENO     RECEIVED DATE   SHIPPED DATE
  ---------- ---------- ----------  -----------     ---------- 
     1112      7000        1004      02-JAN-16      20-DEC-15 
     1113      12345       2002      10-JAN-16      15-DEC-15
     1114      12345       2002      09-JAN-16      14-DEC-15

详细信息

     ONO        PNO         QTY     
  ---------- ---------- ----------  
     1112      12345         3 
     1112      98766         3    
     1113      12345         2
     1114      12345         1

预期答案(当给出特定的 ono/pno 组合时)

记录

   ID_NUMBER       ONO      CNO    DATE_SEND     CONDITION 
  ------------   -------  --------  ----------     --------      
    10001         1112     7000    13-JAN-16     SEND BACK 
    10002         1112     7000    13-JAN-16     SEND BACK
    10003         1112     7000    13-JAN-16     SEND BACK 
    10005         1113     12345    14-JAN-16     SEND BACK
    10006         1114     12345    14-JAN-16     SEND BACK

REMOVED_ODETAILS

     ONO        PNO        QTY
   -------     ------     ------
     1112      12345        2
     1112      98766        1
     1113      12345        1
     1114      12345        1

详细信息

     ONO        PNO         QTY
  ---------- ---------- ----------
    1112       12345          1
    1112       98766          2 
    1113       12345          1

订单

     ONO        CNO       ENO      RECEIVED DATE   SHIPPED DATE
  ---------- ---------- ----------  -----------  ---------- 
    1112       7000        1004      02-JAN-16     20-DEC-15 
    1113       12345       2002      10-JAN-16     15-DEC-15

【问题讨论】:

  • 如果您能提供创建表和插入语句会有所帮助,以便我们可以复制您的表并自己测试。如果您在程序运行后提供了您希望看到的预期输出,这也会有所帮助。
  • 我希望我已经解释得够多了!
  • 很遗憾,您提供的额外信息不足以让我们仅运行您的代码。这就是为什么我要求创建表脚本(包括约束定义,例如主键和外键)和插入语句。想象一下,您不是提出这个问题的人,而是一个无法访问数据库但想要提供帮助的人。您认为您需要哪些信息才能尝试提供帮助?最好的问题带有一个完整的测试用例,其他人可以自己使用和运行,让他们可以玩弄代码直到它工作。
  • 如何将我的表格脚本传递给您?如果可以的话,我们可以通过聊天来完成吗?
  • 只需编辑您的问题并在其中添加信息。如果您使用表/数据的精简版也没关系,只要它们包含足够的信息让我们能够运行您的代码。

标签: plsql oracle11g


【解决方案1】:

以下是我将如何编写程序:

create or replace procedure one_by_one (p_ord_num in number,
                                        p_pro_num in number,
                                        p_qty in number)
is
begin
  insert into records (id_number, ono, cno, date_send, condition)
  select records_seq.nextval,
         o.ono,
         o.cno,
         trunc(sysdate) date_send, -- should include the time? if so, remove the trunc()
         'SEND BACK' condition
  from   orders o
         cross join (select level lvl
                     from   dual
                     connect by level <= p_qty) d
  where  o.ono = p_ord_num
  and    shipped is not null;

  merge into removed_odetails tgt
  using (select p_ord_num ono,
                p_pro_num pno,
                p_qty qty
         from   dual) src
    on (tgt.ono = src.ono and tgt.pno = src.pno)
  when not matched then
    insert (tgt.ono, tgt.pno, tgt.qty)
    values (src.ono, src.pno, src.qty)
  when matched then
    update
    set tgt.qty = tgt.qty + src.qty;

  merge into odetails tgt
  using (select p_ord_num ono,
                p_pro_num pno,
                p_qty qty
         from   dual) src
    on (tgt.ono = src.ono and tgt.pno = src.pno)
  when matched then
    update set tgt.qty = tgt.qty - case when tgt.qty > src.qty then src.qty end
    delete 
    where tgt.qty is null;

  delete from orders
  where ono = p_ord_num
  and   ono not in (select ono
                    from   odetails
                    where  ono = p_ord_num);
end;
/

这里有一个测试用例来证明它的工作原理:

drop procedure one_by_one;
drop table removed_odetails;
drop table records;
drop table odetails;
drop table orders;
drop sequence records_seq;

create table orders (
  ono      number(5) not null primary key,
  cno      number(5), --references customers,
  eno      number(4), --references employees,
  received date,
  shipped  date);

 create table odetails (
  ono      number(5) not null references orders,
  pno      number(5) not null, --references parts,
  qty      integer check(qty > 0),
  primary key (ono,pno));

create table records (
       id_number  number(5) not null primary key,
       ono        number(5) not null,
       cno        number(5) not null,
       date_send  date,
       condition  varchar2(30));

create sequence records_seq
 start with 10001
  maxvalue 999999999999999999999999999
  minvalue 1
  nocycle
  cache 20
  noorder;

create table removed_odetails ( 
      ono      number(5) not null,
      pno      number(5) not null,
      qty      integer check(qty > 0),
      primary key (ono,pno));

insert into orders (ono, cno, eno, received, shipped)
select 1112 ono, 7000 cno, 1004 eno, to_date('02/01/2016', 'dd/mm/yyyy') received, to_date('20/12/2015', 'dd/mm/yyyy') shipped from dual union all
select 1113 ono, 12345 cno, 2002 eno, to_date('10/01/2016', 'dd/mm/yyyy') received, to_date('15/12/2015', 'dd/mm/yyyy') shipped from dual union all
select 1114 ono, 12345 cno, 2002 eno, to_date('09/01/2016', 'dd/mm/yyyy') received, to_date('14/12/2015', 'dd/mm/yyyy') shipped from dual;

insert into odetails (ono, pno, qty)
select 1112 ono, 12345 pno, 3 qty from dual union all
select 1112 ono, 98766 pno, 3 qty from dual union all
select 1113 ono, 12345 pno, 2 qty from dual union all
select 1114 ono, 12345 pno, 1 qty from dual;

commit;

-- now run the procedure for all rows in removed_odetails:

begin
  for rec in (select 1112 ono, 12345 pno, 2 qty from dual union all
              select 1112 ono, 98766 pno, 1 qty from dual union all
              select 1113 ono, 12345 pno, 1 qty from dual union all
              select 1114 ono, 12345 pno, 1 qty from dual)
  loop
    one_by_one(p_ord_num => rec.ono,
               p_pro_num => rec.pno,
               p_qty => rec.qty);
  end loop;
  commit;
end;
/

select * from orders;

       ONO        CNO        ENO RECEIVED   SHIPPED   
---------- ---------- ---------- ---------- ----------
      1112       7000       1004 02/01/2016 20/12/2015
      1113      12345       2002 10/01/2016 15/12/2015

select * from odetails;

       ONO        PNO        QTY
---------- ---------- ----------
      1112      12345          1
      1112      98766          2
      1113      12345          1

select * from removed_odetails;

       ONO        PNO        QTY
---------- ---------- ----------
      1112      98766          1
      1112      12345          2
      1114      12345          1
      1113      12345          1

select * from records;

 ID_NUMBER        ONO        CNO DATE_SEND  CONDITION                     
---------- ---------- ---------- ---------- ------------------------------
     10001       1112       7000 14/01/2016 SEND BACK                     
     10002       1112       7000 14/01/2016 SEND BACK                     
     10003       1112       7000 14/01/2016 SEND BACK                     
     10004       1113      12345 14/01/2016 SEND BACK                     
     10005       1114      12345 14/01/2016 SEND BACK                     

begin
  for rec in (select 1112 ono, 98766 pno, 1 qty from dual)
  loop
    one_by_one(p_ord_num => rec.ono,
               p_pro_num => rec.pno,
               p_qty => rec.qty);
  end loop;
  commit;
end;
/

select * from orders;

       ONO        CNO        ENO RECEIVED   SHIPPED   
---------- ---------- ---------- ---------- ----------
      1112       7000       1004 02/01/2016 20/12/2015
      1113      12345       2002 10/01/2016 15/12/2015

select * from odetails;

       ONO        PNO        QTY
---------- ---------- ----------
      1112      12345          1
      1112      98766          1
      1113      12345          1

select * from removed_odetails;

       ONO        PNO        QTY
---------- ---------- ----------
      1112      98766          2
      1112      12345          2
      1114      12345          1
      1113      12345          1

select * from records;

 ID_NUMBER        ONO        CNO DATE_SEND  CONDITION                     
---------- ---------- ---------- ---------- ------------------------------
     10001       1112       7000 14/01/2016 SEND BACK                     
     10002       1112       7000 14/01/2016 SEND BACK                     
     10003       1112       7000 14/01/2016 SEND BACK                     
     10004       1113      12345 14/01/2016 SEND BACK                     
     10005       1114      12345 14/01/2016 SEND BACK                     
     10006       1112       7000 14/01/2016 SEND BACK   

顺便说一句,你看到上面的测试用例了吗?这就是您应该预先提供的信息类型,以避免浪费那些自愿帮助您的人的时间……免费。当您发布未来的问题时,请注意这一点!

【讨论】:

  • 我已经更新了我的答案;希望这次我的程序能满足您的要求!
  • 我再次更新了我的答案,以确保它在订单/零件在一个已经发回之后被发回的情况下工作。
【解决方案2】:

这是您的代码的一个工作版本(好吧,似乎每次调用只在记录表中添加一行 ono/pno 组合,而不是对应于被删除数量的行数)版本:

create or replace procedure one_by_one( ord_num in number, pro_num in number, quantity number)
as
  cursor cur1
  is
  select * from orders 
  where ono = ord_num;

  cursor cur2
  is
  select * from odetails
  where pno = pro_num
  and   ono = ord_num;

  val1 cur1%rowtype;
  val2 cur2%rowtype;

begin

  open cur1;
  open cur2;

  loop
    fetch cur1 into val1;
    fetch cur2 into val2;

    exit when cur1%notfound;
    exit when cur1%notfound;

    if val1.shipped is not null then
--      if pro_num = val2.pno and ord_num = val2.ono then -- totally unnecessary, since you already did that check as part of the cursor!
      if (val2.qty > quantity) then
        merge into removed_odetails tgt
        using (select ord_num ono,
                      pro_num pno,
                      quantity qty
               from   dual) src
          on (tgt.ono = src.ono and tgt.pno = src.pno)
        when not matched then
          insert (tgt.ono, tgt.pno, tgt.qty)
          values (src.ono, src.pno, src.qty)
        when matched then
          update
          set tgt.qty = tgt.qty + src.qty;

        update odetails
        set    qty = qty - quantity
        where  ono = ord_num
        and    pno = pro_num;

        insert into records(id_number, ono,cno,date_send,condition)
        values(records_seq.nextval, val1.ono,val1.cno,sysdate,'SEND BACK');

      elsif (val2.qty = quantity ) then 
        insert into records(id_number, ono,cno,date_send,condition)
        values (records_seq.nextval, val1.ono,val1.cno,sysdate,'SEND BACK');

        insert into removed_odetails( ono, pno, qty) 
               values(val2.ono,val2.pno, val2.qty);

        delete from odetails where ono = ord_num and pno = pro_num;
      end if;  

      dbms_output.put_line('Item will be send back ');

      delete from orders 
      where ono = ord_num
      and   not exists (select null from odetails where ono = ord_num);

    elsif val1.shipped is null then
      dbms_output.put_line('DO NOTHING ');
    end if;
  end loop;

  close cur1;
  close cur2;
end one_by_one;
/

您试图重新发明MERGE 语句,当您尝试检查removed_odetails 表中是否已经存在一行时,所以我所做的就是删除cursor-for-loop 并进行合并改为语句(并将removed_odetails表的更新更改为更新odetails表)。

不过,我不推荐这种特殊的解决方案;它比我提供的其他解决方案要简单得多。如果你以后需要随时维护这段代码,你会诅咒自己的! (我们都去过那里......)

【讨论】:

  • 非常感谢您的耐心和帮助!真的很感激。不过,我希望你能再对我耐心一点。问题,你认为游标可以做MERGE语句吗?然后在游标内可以检查REMOVED_ODETAILS是否为空,否则插入是否更新?只是想知道!
  • 可能,但是当您使用代码时,保持简单确实是值得的。代码越简单,维护、调试和理解就越容易,而且很可能,它的性能会比更复杂的代码好得多(尽管显然总是有例外!)。我真的不能强调这一点!
  • 我理解,可能是我缺乏经验,因为这是我第一次在 PL/SQL 中工作
  • {nods} 我认为您的思维方式非常程序化(也就是逐行)。当涉及到数据库时,它在执行集合操作方面非常强大 - 例如。不要去“这是一个游标,获取一行,插入该行,获取下一行,插入该行,等等......”,因为你可以只做“插入 tablex 选择......”。更快、更好、更容易阅读、更少的代码等等。如果你习惯于程序化思考,那么很难转向基于集合的思考!也许this blog post 会有用?
  • 我完全同意你的看法,我喜欢 DML 快速、准确和代码高效。但是,我正在尽我最大的努力两者兼得,虽然还是新手,但正在学习:)
猜你喜欢
  • 1970-01-01
  • 2016-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多