PLSQL Oracle Cursor in procedure - oracle

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;

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;

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;

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

Related

I'm trying to get a process to be kicked off when a query finds a table is empty

I want to code a way to check if there's data in a table prior to executing a stored procedure.
I've created some 'stripped down' test code that 'almost' meets the conditions that I seek, I was hoping someone might be able to help me get that to work. If so, then I can substitute the values for my procedure, instead of just the dbms_output and be up and running.
Creates a test table, with no rows.
CREATE TABLE t (c VARCHAR2(20));
Then the way I am trying to do this...
declare
no_such_table exception;
pragma exception_init( no_such_table, -942 );
EXISTS_1 integer;
BEGIN
for tst in (
select count (c) x from t
)
loop
execute immediate' select count (c) Z from ' ||tst.t into EXISTS_1;
if EXISTS_1 <= 0
then
dbms_output.put_line( 'a' );
else dbms_output.put_line( 'b' );
end if;
end loop;
exception
when no_such_table
then
dbms_output.put_line( 'c' );
WHEN NO_DATA_FOUND THEN
dbms_output.put_line( 'd' );
end;
The first part, with the count, is supposed to hold a numeric value to indicate if there are any rows in the table. Then the execute immediate into EXISTS_1 holds the value to decide what output to give.
Firstly, I can't get the execute immediate bit to actually work. But if I could get it working, I want the output to record 'a' if there's no rows in the table. (Actually, I would execute the procedure here) and to record 'b' if there was data in there, which you can insert with:
insert into t (c) values('x');
commit;
The 'c' and 'd' outputs are just attempts to handle other potential issues that may occur.
As things stand, I get an error indicating that component t must be declared. Can you understand what I 'm trying to do, and if so, hopefully suggest a means to achieve my goal please?
your first select in a loop will always return one row, so there now Need to do a loop.
using tst.t in a Loop is not possible.
is that what are you looking for?
declare
x number := 0;
begin
select count(c) into x from t;
if x <= 0
then
dbms_output.put_line( 'a' );
else
dbms_output.put_line( 'b' );
end if;
end;

PLSQL IMPLICIT CURSOR No Data Found After CURSOR

I have a Main cursor that is working fine.
declare
v_firm_id number;
amount number;
v_total_sum TABLE_TEMP.TOTAL_SUM%TYPE;
CURSOR MT_CURSOR IS
SELECT firm_id FROM t_firm;
BEGIN
OPEN MT_CURSOR;
LOOP
FETCH MT_CURSOR INTO v_firm_id;
EXIT WHEN MT_CURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(to_char(sysdate, 'mi:ss') ||'--- '|| v_firm_id)
INSERT INTO TABLE_TEMP(TOTAL_SUM) VALUES(v_firm_id)
COMMIT;
END LOOP;
DBMS_LOCK.SLEEP(20);
BEGIN
FOR loop_emp IN
(SELECT TOTAL_SUM INTO v_total_sum FROM TABLE_TEMP)
LOOP
dbms_output.put_line(to_char(sysdate, 'mi:ss') ||'--- '|| v_total_sum || '-TEST--');
END LOOP loop_emp;
END;
end;
Everything Works fine except dbms_output.put_line(v_total_sum || '---');
I do not get any data there. I get the correct number of rows. which it inserted.
The problem is the cursor FOR loop has a redundant into clause which it appears the compiler silently ignores, and so v_total_sum is never used.
Try this:
begin
for r in (
select firm_id from t_firm
)
loop
insert into table_temp (total_sum) values (r.firm_id);
end loop;
dbms_lock.sleep(20);
for r in (
select total_sum from table_temp
)
loop
dbms_output.put_line(r.total_sum || '---');
end loop;
commit;
end;
If this had been a stored procedure rather than an anonymous block and you had PL/SQL compiler warnings enabled with alter session set plsql_warnings = 'ENABLE:ALL'; (or the equivalent preference setting in your IDE) then you would have seen:
PLW-05016: INTO clause should not be specified here
I also moved the commit to the end so you only commit once.
To summarise the comments below, the Cursor FOR loop construction declares, opens, fetches and closes the cursor for you, and is potentially faster because it fetches in batches of 100 (or similar - I haven't tested in recent versions). Simpler code has less chance of bugs and is easier to maintain in the future, for example if you need to add a column to the cursor.
Note the original version had:
for loop_emp in (...)
loop
...
end loop loop_emp;
This is misleading because loop_emp is the name of the record, not the cursor or the loop. The compiler is ignoring the text after end loop although really it should at least warn you. If you wanted to name the loop, you would use a label like <<LOOP_EMP>> above it. (I always name my loop records r, similar to the i you often see used in numeric loops.)

How to resolve fetch out of sequence in oracle?

I have a procedure in which I am often getting the following error in oracle 11g:
ORA-01002: fetch out of sequence ORA-06512:
at "LEAVE.GES_ERP_LEV_FFS_INTERFACE_PRC", line 350 ORA-06512: at line 1.
at line 350 I have-
BEGIN
FOR V_INTERFACE_EMP IN CUR_INTERFACE_EMP LOOP (Line 350)
EXIT WHEN CUR_INTERFACE_EMP%NOTFOUND;
V_ERR_FLAG := 'N';
V_LOCAL_EMP := 'Y';
BEGIN
The Cursor CUR_INTERFACE_EMP is declared as below
SELECT GELF.*
FROM GES_ERP_LEV_FFS_INTERFACE_T GELF
WHERE (GELF.BALANCE_FLAG != 'W'
OR GELF.CASE_FLAG = 'S'
OR SELF.BALANCE_FLAG IS NULL)
AND GELF.PROCESS_FLAG = 'N'
AND GELF.DATE_OF_RELEASE <= TRUNC(SYSDATE);
If i update some records of the table with Process_Flag Y,the batch works fine for some time and then again after some days we get this same issue.
Please help,let me know in case data is also needed for the mentioned table.
If i update some records of the table with Process_Flag Y,the batch
works fine for some time and then again after some days we get this
same issue.
You try to fetch from a SELECT FOR UPDATE, however a COMMIT has already been issued before it.
I think you have a COMMIT somewhere INSIDE the LOOP which is causing this issue.
A quote by Tom Kyte here:
for x in ( select rowid rid, t.* from T ) loop
update T set x = x+1 where rowid = x.rid;
commit;
end loop;
That implicit cursor is fetched from "across a commit". It is the
practice of keeping a cursor open after committing. It is a bad
practice and is a common cause of ORA-1555 (the above looping
construct in particular)
Also, you are using a CURSOR FOR LOOP. The CURSOR FOR LOOP will terminate when all of the records in the cursor have been fetched. So, you don't need to EXIT explicitly.
You could simply do it as:
FOR V_INTERFACE_EMP IN CUR_INTERFACE_EMP
LOOP
V_ERR_FLAG := 'N';
V_LOCAL_EMP := 'Y';
...
END LOOP;
I was facing the same issue of fetch out of sequence in oracle plsql.
The problem was below line of code which was used in a procedure that was getting called from inside the loop.
-------------problem code--------
if p_ret is null
then
p_ret := 'SUCCESS';
rollback;
return;
end if;
-------------problem code--------
I should not have used rollback.
So, watch out for rollback and commit inside the loop statement.

Cursor occasionally lose data when open in run time

This is my cursor with SELECT statement which always return right result. But when i use it in a procedure, it sometimes get wrong result (return only one row).
This problem appears with a large frequency (~5-10 times per 1000 procedure call). Maybe this is known issues with oracle 10gR2 or some disadvantages in my procedure.
I hope I can understand why this happening and get the better solutions because I get ~5000 procedure calls per day.
This is my cursor which declared in procedure:
CURSOR cur_result (var_num NUMBER)
IS
SELECT
m_username,
m_password
FROM
M_USERS
WHERE
m_status = 1
AND rownum <= var_num;
This is my procedure body:
p_num:=5;
-- the IF statement for guarantee the SELECT statement always
-- get more rows than p_num
LOCK TABLE M_USERS IN EXCLUSIVE MODE;
OPEN cur_result (p_num);
LOOP
FETCH
cur_result
INTO
p_username,
p_password;
-- the problems here: only 1 row is queried
-- the LOOP will exit after second time fetch
IF (cur_result%NOTFOUND AND (cur_result%ROWCOUNT < p_num) ) THEN
iserror := 1;
EXIT;
END IF;
EXIT WHEN cur_result%NOTFOUND;
UPDATE
M_USERS
SET
m_status = 2
WHERE
m_username = p_username
AND m_password = p_password;
END LOOP;
CLOSE cur_result;
Thanks in advance.

Find out last executed query in oracle

I am working with an web based application, Where i have to do some customizations. But i dont have access to Java Source code and even JSP's. I can access only database.
Is there a any way to find out last executed query.
Simply there are many triggers on tables(They fires when i do something from frontend), I want to know which query is causing the trigger to fire. Is there a way to find them. Either inside a trigger or some other oracle views or table.
You can use the below example in the trigger to find the statement executed
create or replace TRIGGER tbuTAR_TSM
BEFORE UPDATE ON TAR_TSM
FOR EACH ROW
declare
v_test varchar2(10000);
cursor c1 is
select ltrim(sq.sql_text)
into v_test
from v$sql sq, v$session se, v$open_cursor oc
where sq.sql_id = oc.sql_id
and se.saddr = oc.saddr
and se.sid = oc.sid
and se.audsid = SYS_CONTEXT('userenv', 'sessionid')
order by oc.LAST_SQL_ACTIVE_TIME desc;
begin
open c1;
loop
fetch c1
into v_test;
exit when c1%notfound;
if substr(upper(v_test), 1, 6) = 'UPDATE' then
if instr(upper(v_test), 'TAR_TSM') != 0 then
dbms_output.put_line(v_test);
exit;
end if;
end if;
end loop;
close c1;
end;

Resources