Kacper Werema
Kacper Werema

Reputation: 448

PLSQL Oracle Cursor in procedure

currently I am learning PLSQL, using Oracle. I am trying to get data which will be older than PARAM days decalred in another table. I want the procedure to take all the data, check if some records are older (recv_date) than parameter from param_value and if yes than fire my alarm procedure. I have problem with declaring a CURSOR and ln_pallets_container. I know I could somehow get into ln_pallets data only WHERE the recv_date I already filtered but neither here I have no idea how to do it correctly. Maybe I should declare cursor before procedure and not inside of it?

  procedure CHECK_STOCK_DATE(warehouse_id_in IN warehouse.warehouse_id%TYPE)
IS
ln_pallet_count NUMBER;
ln_days_till_expiration param_value.param_value%TYPE;

CURSOR ln_pallets IS
       SELECT container_id, recv_date
       FROM wms_stock ws

ln_pallets_container%ROWTYPE;


BEGIN

       OPEN ln_pallets;
            LOOP
                FETCH ln_pallets INTO ln_pallets_container;
                EXIT WHEN ln_pallets%NOTFOUND;

                SELECT param_value.param_value
                INTO ln_days_till_expiration
                FROM param_value
                WHERE param_value.parameter_id = 266;

                   IF(ln_pallets_container.recv_date >= trunc(sysdate - ln_days_till_expiration)

                      ALARM.ALARM(WAREHOUSE_ID =>MY_COMMONS.GET_WHRS_ID,
SOURCE_TEXT => ln_pallets_container.container_id,
MESSAGE_CODE => 'Cannot find this container on warehouse. Check container code.');
                  END IF;
            END LOOP;
        CLOSE ln_pallets;
END;

Upvotes: 0

Views: 27178

Answers (3)

Boneist
Boneist

Reputation: 23588

There are several things wrong with your code, which I've fixed and highlighted in the following:

PROCEDURE check_stock_date(warehouse_id_in IN warehouse.warehouse_id%TYPE) IS
  ln_pallet_count         NUMBER;
  ln_days_till_expiration param_value.param_value%TYPE;

  CURSOR ln_pallets IS
    SELECT container_id,
           recv_date
    FROM   wms_stock ws; -- added semicolon

  ln_pallets_container ln_pallets%ROWTYPE; -- amended to set the datatype of the variable to be the cursor rowtype

BEGIN

  OPEN ln_pallets;
  LOOP
    FETCH ln_pallets
      INTO ln_pallets_container;
    EXIT WHEN ln_pallets%NOTFOUND;

    SELECT param_value.param_value
    INTO   ln_days_till_expiration
    FROM   param_value
    WHERE  param_value.parameter_id = 266;

    IF /*removed bracket*/ ln_pallets_container.recv_date >= trunc(SYSDATE - ln_days_till_expiration)
    THEN --added

      alarm.alarm(warehouse_id => my_commons.get_whrs_id,
                  source_text  => ln_pallets_container.container_id,
                  message_code => 'Cannot find this container on warehouse. Check container code.');
    END IF;
  END LOOP;
  CLOSE ln_pallets;
END check_stock_date;

However, this could be done much more efficiently. Currently, you are looping through all the rows in wms_stock, plus you are explicitly opening, fetching and closing the cursor yourself.

That means for every row in wms_stock, you are finding the value of parameter_id 266 (which I assume won't change whilst you're looping through the results!), as well as doing the check to see if you can run your alarm procedure.

Instead of fetching all the rows, why not move the check into the cursor - that way, you'll only be fetching the parameter 266 value once and filtering out any rows that don't need to have the alarm procedure run.

At the same time, why not switch to using a cursor-for-loop? That way, you don't have to worry about opening/fetching from/closing the cursor, as Oracle handles all that for you.

Doing that will result in far less code, which happens to be more efficient and easier to read and maintain, like so:

PROCEDURE check_stock_date(warehouse_id_in IN warehouse.warehouse_id%TYPE) IS 
BEGIN
  FOR ln_pallets_rec IN (SELECT container_id,
                                recv_date
                         FROM   wms_stock ws
                         WHERE  recv_date >= (SELECT trunc(SYSDATE - param_value.param_value
                                              FROM   param_value
                                              WHERE  param_value.parameter_id = 266))
  LOOP
    alarm.alarm(warehouse_id => my_commons.get_whrs_id,
                source_text  => ln_pallets_rec.container_id,
                message_code => 'Cannot find this container on warehouse. Check container code.');
  END LOOP;

END check_stock_date;

Upvotes: 2

Rene
Rene

Reputation: 10551

Fixed some issues in your code.

procedure check_stock_date(warehouse_id_in in warehouse.warehouse_id%type) is

       ln_pallet_count         number;
       ln_days_till_expiration param_value.param_value%type;

       l_container_id wms_stock.container_id%type;
       l_recv_date    wms_stock.recv_date%type;

       cursor ln_pallets is
          select container_id
                ,recv_date
            from wms_stock ws;

    begin

       open ln_pallets;

       loop
          fetch ln_pallets
             into l_container_id
                 ,l_recv_date;

          exit when ln_pallets%notfound;

          select param_value.param_value
            into ln_days_till_expiration
            from param_value
           where param_value.parameter_id = 266;

          if l_recv_date >= trunc(sysdate - ln_days_till_expiration)
          then
             alarm.alarm(warehouse_id => my_commons.get_whrs_id
                        ,source_text  => l_container_id
                        ,message_code => 'Cannot find this container on warehouse. Check container code.');
          end if;
       end loop;
       close ln_pallets;
    end;

Upvotes: 1

Seif
Seif

Reputation: 86

Hi you don't specify table name for ln_pallets_container variable also it's missing a ';' after cursor declaration fix this and try

Upvotes: 0

Related Questions