inserting data select into with loop in plsql - oracle

I am just want to enter forms detail column data into a database column from a detail block and that must check first entered data which it already have in that column when i entered data data saves in other tables but in token_staus is not entering data and message me No data found for that i write a loop which is not working for me i am making some kind of mistake obviously but not sure where it is
DECLARE
TOKEN_NO NUMBER;
TOKEN_STATUS1 NUMBER;
--TOKEN_STATUS2 := :TOKEN_STATUS;
BEGIN
SELECT SR_NO, TOKEN_STATUS INTO TOKEN_NO, TOKEN_STATUS1 FROM LOOPT2 WHERE SR_NO = :TOKEN_NO;
--IF :TOKEN_STATUS IS NULL
--THEN
LOOP
GO_BLOCK('TOKEN_REC2');
FIRST_RECORD;
INSERT INTO LOOPT2(TOKEN_STATUS) VALUES(:TOKEN_STATUS);
NEXT_RECORD;
EXIT WHEN :SYSTEM.LAST_RECORD = 'TRUE';
END LOOP;
--END IF;
EXCEPTION
when others then
message (sqlerrm);
END;
Please help me
Little Foot please

If I understood you correctly, it would be something like this (check comments within the code): key thing here seems to be a BEGIN-EXCEPTION-END block within the LOOP.
DECLARE
TOKEN_NO NUMBER;
TOKEN_STATUS1 NUMBER;
BEGIN
GO_BLOCK('TOKEN_REC2');
FIRST_RECORD;
LOOP
BEGIN
SELECT SR_NO, TOKEN_STATUS
INTO TOKEN_NO, TOKEN_STATUS1
FROM LOOPT2
WHERE SR_NO = :TOKEN_NO;
-- if such a :TOKEN_NO exists, that SELECT will return some values
-- which means that you want to skip it, so - don't do anything
EXCEPTION
-- if such a :TOKEN_NO does not exist, SELECT will return
-- NO_DATA_FOUND which means that you want to perform insert
WHEN NO_DATA_FOUND THEN
INSERT INTO LOOPT2 (TOKEN_STATUS)
VALUES (:TOKEN_STATUS);
WHEN TOO_MANY_ROWS THEN
NULL;
END;
EXIT WHEN :SYSTEM.LAST_RECORD = 'TRUE';
NEXT_RECORD;
END LOOP;
END;

Related

Validation of result before executing the query

When running a stored procedure to fetch some rows, First I want to validate if the query will return a row before sending the result, and second if it is possible to validate without running the same query twice.
I am using a cursor to store the yielded result, So I tried the cursor attribute %ROWCOUNT & %NOTFOUND. But the doesnt quite work. Plus I want to do this without running a loop on the cursor.
procedure MODULE_LIST_GK(p_module_Id IN MODULE_LIST.MODULE_ID% TYPE,
p_Error_Code out nvarchar2,
p_Error_Msg out nvarchar2,
p_Cursor out sys_refcursor) IS
BEGIN
OPEN p_Cursor FOR
SELECT A.MODULE_ID,
A.MODULE_NM,
A.AUTH_STATUS_ID
FROM MODULE_LIST A
WHERE A.MODULE_ID=p_module_Id;
SELECT COUNT(MODULE_ID)
INTO v_row_num
FROM MODULE_LIST A
WHERE A.MODULE_ID=p_module_Id;
IF v_row_num=0 THEN
p_Error_Code := SQLCODE;
p_Error_Msg := 'Does not Exists';
Return;
end IF;
EXCEPTION
WHEN OTHERS THEN
p_error_code:= SQLCODE;
p_error_msg := SQLERRM;
END MODULE_LIST_GK;
Your implementation have several points that could be improved.
First if you expect that for lot of parameters the returned cursor will be empty,
than first check the empty cursor and only after this check open the cursor. You do it vice versa.
How to check if the cursor is empty? Unfortunatelly you must fetch the first row to be able to verify it.
open l_cur for
select id, status from tab where id = p_id;
fetch l_cur into l_id, l_status;
if l_cur%NOTFOUND then
p_Error_Msg := 'Does not Exists';
Return;
end if;
This check is far more effective that the often used count(*) as it is considering only the first (few) rows and not counting all rows in the cursor.
If the check fails you are ready, othervise simple open the cursor and return it.
open l_cur for
select id, status from tab where id = p_id;
p_Cursor := l_cur;
Two additional thinks come to mind.
You should rething the generall approach if the database is very dynamic. How would you handle the case when other session deletes some row between the check and the second open of the cursor?
Finally consider returning an exception instead of the return code.
In order to know whether a cursor contains rows, you must open it and fetch the first row. Once you've done this, it makes no sense anymore to return that cursor, for the recipient will not be able to fetch that first row, because the cursor already points beyond it.
So, you must select twice. What you'd do is to use ROWNUM or an EXISTS clause here to show the DBMS that you are not interested in any more rows. This can speed up the query extremely.
PROCEDURE module_list_gk(p_module_id IN MODULE_LIST.MODULE_ID%TYPE,
p_error_code OUT NVARCHAR2,
p_error_msg OUT NVARCHAR2,
p_cursor OUT SYS_REFCURSOR) IS
v_count INTEGER;
BEGIN
SELECT COUNT(*)
INTO v_count
FROM module_list
WHERE module_id = p_module_Id
AND ROWNUM = 1;
IF v_count = 0 THEN
p_error_code := 0; -- Or -1403 for NO DATA FOUND if you like
p_error_msg := 'Does not Exists';
RETURN;
END IF;
OPEN p_Cursor FOR
SELECT module_id, module_nm, auth_status_id
FROM module_list
WHERE module_id = p_module_id;
EXCEPTION WHEN OTHERS THEN
p_error_code:= SQLCODE;
p_error_msg := SQLERRM;
END module_list_gk;
SQLCODE for the first query will be 0 by the way (SELECT COUNT(*) returns one row with the number of records found - no error hence). This is why you should decide to either return zero explicitly or some error code, such as -1403.
Here is the same with EXISTS:
BEGIN
SELECT CASE WHEN EXISTS
(
SELECT NULL
FROM module_list
WHERE module_id = p_module_Id
) THEN 1 ELSE 0 END
INTO v_count
FROM DUAL;
IF v_count = 0 THEN

Why are none of my conditions being met in this proc?

So i have a stored procedure (that's been watered down below for demo purposes) that aren't passing any conditions and thus aren't inserting/passing any values into my table. I've tried converting the varchar/string that is being passed in by Java to a number but nothing is working. Below is my 'simplified code'
Create or Replace Procedure SAMPLE(rValue IN VARCHAR)
IS
v_Max value.value%type;
v_forecast value.value%type;
BEGIN
--
SELECT BUFFER_MAX_VALUE
INTO v_MAX
FROM look_up;
--
EXCEPTION
WHEN no_data_found
THEN SELECT 0
INTO v_forecast
FROM DUAL;
--
IF to_Number(rValue) < 0 OR to_Number(rValue) > v_MAX)
THEN
dbms_output.put_line('IF1 Works');
insert into value(value_id, value)
values(1, rValue);
ELSIF rValue is null OR to_Number(rValue) = 0
THEN
dbms_output.put_line('IF1A ONLY Works');
END IF;
ELSE
insert into value(value_id, value)
values(1, v_forecast);
dbms_output.put_line('IF1 ELSE ONLY Works');
END SAMPLE;
i tried passing the following in:
BEGIN
SAMPLE('-7');
END;
If the first SELECT BUFFER_MAX_VALUE returns anything, nothing else will be executed because you put absolutely everything into the EXCEPTION section. If you meant to handle that statement only, you should have enclosed it into its own BEGIN-END block, such as
create procedure ...
begin
-- its own begin starts now
begin
select buffer_max_value into v_max
from look_up;
exception
when no_data_found then
-- do something here
end;
-- its own end ends now
-- put the rest of your code here
end;
By the way, does LOOK_UP table contain no rows or only one row, always? Because, as SELECT you wrote contains no WHERE clause, it might raise TOO_MANY_ROWS (which you should also handle).
You declared rValue as VARCHAR2, and then apply TO_NUMBER to it. Why don't you declare it to be a NUMBER, instead? Because, nothing prevents you from passing, for example, 'XYZ' to the procedure, and then TO_NUMBER will miserably fail with the INVALID NUMBER error.
[EDIT: some more exception handling]
EXCEPTION section handles all exceptions that happen in that BEGIN-END block, no matter how many SELECT statements you have. Though, you won't know which one failed, unless you include a little bit of additional (simple) programming.
Note that this is just for showing what I meant; don't handle errors with DBMS_OUTPUT (as, most probably, nobody will see it), and rarely you'd want to handle errors with WHEN OTHERS.
create procedure ...
l_position number;
begin
l_position := 1;
select ... into ... from ...;
l_position := 2;
select ... into ...
exception
when others then
dbms_output.put_line('Error on position ' || l_position ||' '|| sqlerrm);
raise;
end;
As far as I can tell, you wanted the exception section to trap the situation where there is nothing in the lookup table. In that case, you set v_forecast and then continue. That means you need to put the select inside its own block.
I also avoiding multiple to_number calls by setting a constant.
I got rid of the unnecessary select from dual.
I also really really hope that you do not have a table named VALUE with a column named VALUE. Choose more meaningful names.
See how this works for you.
CREATE OR REPLACE PROCEDURE sample (rvalue IN VARCHAR2)
IS
c_rvalue CONSTANT NUMBER := rvalue;
v_max VALUE.VALUE%TYPE;
v_forecast VALUE.VALUE%TYPE;
BEGIN
BEGIN
SELECT buffer_max_value INTO v_max FROM look_up;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
v_forecast := 0;
END;
IF c_rvalue < 0 OR c_rvalue > v_max
THEN
DBMS_OUTPUT.put_line ('IF1 Works');
INSERT INTO VALUE (value_id, VALUE)
VALUES (1, rvalue);
ELSIF c_rvalue IS NULL OR c_rvalue = 0
THEN
DBMS_OUTPUT.put_line ('IF1A ONLY Works');
ELSE
INSERT INTO VALUE (value_id, VALUE)
VALUES (1, v_forecast);
DBMS_OUTPUT.put_line ('IF1 ELSE ONLY Works');
END IF;
END sample;

PL/SQL -If exists in Table type of varchar2 not working

I am using a huge table which loops inside a cursor so i thought rather than querying on each iteration, put the particular data in a Table type and then check data exists inside the loop.
declare
type type_product_list is table of varchar(6);
product_list type_product_list;
begin
SELECT distinct(PRODUCT_NUMBER)
BULK COLLECT INTO product_list
FROM WEB_PRODUCTS web WHERE some conditions;
FOR i IN 1..product_list.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(product_list(i)); -- This line printing properly.
END LOOP;
IF product_list.EXISTS('00029') THEN -- This condition always fails
DBMS_OUTPUT.PUT_LINE('Found');
ELSE
DBMS_OUTPUT.PUT_LINE('Not Found');
END IF;
end;
Output
00029
00030
00031
00032
..... other data
NOT FOUND
Please help, how can I get the IF block executed.
*Update
Main purpose of this problem is to call a function inside the IF block, and if block itself will be inside a cursor loop which gives dynamic product id in each iteration, i.e. if the product id exists then call that function.
begin
SELECT distinct(PRODUCT_NUMBER)
BULK COLLECT INTO product_list
FROM WEB_PRODUCTS web WHERE some conditions;
OPEN cur_cms_scriptdtl();
LOOP
FETCH cur_cms_scriptdtl INTO productId, productName;
EXIT WHEN cur_cms_scriptdtl%notfound;
IF product_list.EXISTS(productId) THEN
-- Function call
END IF;
END LOOP;
CLOSE cur_cms_scriptdtl;
After some digging and help from Oracle docs, I was able to get it without FOR LOOP.
The helping angel here is : member of
BEGIN
SELECT distinct(PRODUCT_NUMBER)
BULK COLLECT INTO product_list
FROM WEB_PRODUCTS web WHERE some conditions;
OPEN cur_cms_scriptdtl();
LOOP
FETCH cur_cms_scriptdtl INTO productId, productName;
EXIT WHEN cur_cms_scriptdtl%notfound;
IF productId member of product_list THEN
-- Function call
END IF;
END LOOP;
CLOSE cur_cms_scriptdtl;
In collections, exists method receives as parameter an index, not a value.
IF product_list(i) = '00029' THEN
Try this:
DECLARE
type type_product_list is table of varchar(6);
product_list type_product_list;
vFound BOOLEAN := false;
BEGIN
FOR i IN 1..product_list.COUNT LOOP
dbms_output.put_line(product_list(i));
IF product_list(i) = '00029' THEN
vFound := true;
exit;
END IF;
END LOOP;
IF vFound THEN
dbms_output.put_line('Found');
ELSE
dbms_output.put_line('Not Found');
END IF;
END LOOP;

Oracle PL/SQL function: if parameter not in table column => continue program

Please see my code below. If the parameter p_cust_id is not in the column cust_id I want "0" to be printed out (which means not logged in). If it is in cust_id I want oracle to continue the second part of the code (the part below the empty row).
I have tried to solve this by inserting the values of column cust_id in a cursor and then inserting it in the variable v_cust_id. Perhaps this results in unnecessarily much code?
My problem is that the program does not seem to run the second part even if p_cust_id is in cust_id. It just prints out "0" even though the customer ID and the password are correct.
Before I added the cursor the program worked as it was supposed to, unless the parameter p_cust_id didn't match any value in the cust_id column. If this was the case nothing was printed out.
create or replace function log_in(
p_cust_id in customer.cust_id%type,
p_passwd in customer.passwd%type)
return varchar2
as
cursor c_cust_id is select cust_id from customer;
v_cust_id customer.cust_id%type;
v_passwd customer.passwd%type;
v_logged_in number(1);
v_not_logged_in number(1);
begin
v_logged_in := 1;
v_not_logged_in := 0;
if not c_cust_id%isopen then
open c_cust_id;
end if;
loop
fetch c_cust_id
into v_cust_id;
exit when c_cust_id%notfound;
end loop;
if p_cust_id not in(v_cust_id) then
return v_not_logged_in;
end if;
close c_cust_id;
select passwd
into v_passwd
from customer
where cust_id = p_cust_id;
if v_passwd = p_passwd then
return v_logged_in;
else
return v_not_logged_in;
end if;
end;
I could see, you don't need a cursor at all, to check if the cust_id is in the table. Just search for the cust_id in the table, and attempt to fetch the password. If it exists, you get the value, and NO_DATA_FOUND exception otherwise which means not logged in.
BEGIN
select passwd
into v_passwd
from customer
where cust_id = p_cust_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
return v_not_logged_in;
END;
Full code will be:
create or replace function log_in(
p_cust_id in customer.cust_id%type,
p_passwd in customer.passwd%type)
return varchar2
as
v_cust_id customer.cust_id%type;
v_passwd customer.passwd%type;
v_logged_in number(1);
v_not_logged_in number(1);
begin
v_logged_in := 1;
v_not_logged_in := 0;
BEGIN
select passwd
into v_passwd
from customer
where cust_id = p_cust_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
return v_not_logged_in;
END;
if v_passwd = p_passwd then
return v_logged_in;
else
return v_not_logged_in;
end if;
end;

Are there alternative methods for saying 'next' in a pl/sql for loop?

So I've got a for loop that processes a list of IDs and has some fairly complex things to do. Without going into all the ugly details, basically this:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
...snip...
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
-- do some data checking stuff...
-- here we will look for duplicate entries, so we can noop if duplicate is found
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = :P4_ID AND county_id = v_county_id;
-- NEXT;! NOOP;! but there is no next!
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('no dups found, proceeding');
END;
-- here we have code we only want to execute if there are no dupes already
IF v_dup_check IS NULL THEN
-- if not a duplicate record, proceed...
ELSE
-- reset duplicate check variable
v_dup_check := NULL;
END;
END LOOP;
END;
How I normally handle this is by selecting into a value, and then wrap the following code in an IF statement checking to make sure that duplicate check variable is NULL. But it's annoying. I just want to be able to say NEXT; or NOOP; or something. Especially since I already have to catch the NO_DATA_FOUND exception. I suppose I could write a letter to Oracle, but I'm curious how others handle this.
I could also wrap this in a function, too, but I was looking for something a little cleaner/simpler.
Oracle 11g adds a C-style "continue" loop construct to PL/SQL, which syntactically sounds like what you're looking for.
For your purposes, why not just eliminate the duplicates prior to entering the loop? This could be done by querying l_selected using a table function, and then filtering out records you don't want instead of iterating over every value. Something like...
declare
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
cursor no_dups_cur (p_selected APEX_APPLICATION_GLOBAL.VC_ARR2) is
select * from (
select selected.*,
count(*) over (partition by county_id) cnt -- analytic to find counts grouped by county_id
from table(p_selected) selected -- use table function to treat VC_ARR2 like a table
) where cnt = 1 -- remove records that have duplicate county_ids
;
begin
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
for i in no_dups_cur(l_selected) loop
null; -- do whatever to non-duplicates
end loop;
end;
Just substitute the logic for determining a "duplicate" with your own (didn't have enough info from your example to really answer that part)
Instead of catching NO_DATA_FOUND, how about SELECTing the number of matching entries into a variable, say l_count, and proceeding if this count works out to be zero? Something like the following:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
l_count INTEGER;
...snip...
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
-- do some data checking stuff...
-- here we will count duplicate entries, so we can noop if duplicate is found
SELECT COUNT(*) INTO l_count FROM org_county_accountable
WHERE organization_id = :P4_ID AND county_id = v_county_id;
IF l_count = 0 THEN
-- here we have code we only want to execute if there are no dupes already
-- if not a duplicate record, proceed...
END IF;
END LOOP;
END;
To count the number of rows is also possible (see Pourquoi Litytestdata) but you can also do what you want to do in the when_no_data_found exception block.
declare
l_selected apex_application_global.vc_arr2;
l_county_id org_county_accountable.count_id%type;
begin
l_selected := apex_util.string_to_table(:p4_select_lst);
for i in l_selected.first..l_selected.last loop
begin
select count_id
into l_county_id
from org_county_accountable
where organization_id = :p4_id
and county_id = v_county_id;
exception
when no_data_found then
-- here we have code we only want to execute if there are no dupes already
-- if not a duplicate record, proceed...
end;
end loop;
end;
<xmp>
<<next_loop>>
loop
...
...
if ....
then
goto next_loop;
</xmp>
This is a case where a GOTO statement might be useful. See the Oracle Documentation in the control structures to see how to do this. Also, you may want to search around here to find out how to query for the existence of a record. Running a query and waiting for an exception isn't optimal.
Another way - turn the check into a local function:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
...snip...
FUNCTION dup_exists
( p_org_id org_county_accountable.organization_id%TYPE
, p_county_id org_county_accountable.county_id%TYPE
) RETURN BOOLEAN
IS
v_dup_check org_county_accountable.county_id%TYPE;
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = p_org_id AND county_id = p_county_id;
RETURN TRUE;
EXCEPTION WHEN NO_DATA_FOUND THEN
RETURN FALSE;
END;
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
-- do some data checking stuff...
-- here we have code we only want to execute if there are no dupes already
IF NOT dup_exists (:P4_ID, v_county_id) THEN
-- if not a duplicate record, proceed...
END;
END LOOP;
END;
Of course, the local function could be re-written to use the count method if you prefer:
FUNCTION dup_exists
( p_org_id org_county_accountable.organization_id%TYPE
, p_county_id org_county_accountable.county_id%TYPE
) RETURN BOOLEAN
IS
l_count INTEGER;
BEGIN
SELECT COUNT(*) INTO l_count
FROM org_county_accountable
WHERE organization_id = p_org_id AND county_id = p_county_id;
RETURN (l_count > 0);
END;
Another method is to raise and handle a user-defined exception:
DECLARE
l_selected APEX_APPLICATION_GLOBAL.VC_ARR2;
duplicate_org_county EXCEPTION;
...snip...
BEGIN
-- get the list ids
l_selected := APEX_UTIL.STRING_TO_TABLE(:P4_SELECT_LIST);
-- process each in a nice loop
FOR i IN 1..l_selected.count
LOOP
BEGIN
-- do some data checking stuff...
-- here we will look for duplicate entries, so we can noop if duplicate is found
BEGIN
SELECT county_id INTO v_dup_check FROM org_county_accountable
WHERE organization_id = :P4_ID AND county_id = v_county_id;
RAISE duplicate_org_county;
EXCEPTION WHEN NO_DATA_FOUND THEN
dbms_output.put_line('no dups found, proceeding');
END;
-- here we have code we only want to execute if there are no dupes already
EXCEPTION
WHEN duplicate_org_county THEN NULL;
END;
END LOOP;
END;
I wouldn't normally do this, but if there were half a dozen reasons to jump to the next record, this might be preferable to multiple nested IFs.
I know this is an oldie but I couldn't help notice that none of the answers above take into account the cursor attributes:
There are four attributes associated with cursors: ISOPEN, FOUND, NOTFOUND, and ROWCOUNT. These attributes can be accessed with the % delimiter to obtain information about the state of the cursor.
The syntax for a cursor attribute is:
cursor_name%attribute
where cursor_name is the name of the explicit cursor.
So in this case you could use ROWCOUNT (which indicates the number of rows fetched so far) for your purposes, like this:
declare
aux number(10) := 0;
CURSOR cursor_name is select * from table where something;
begin
select count(*) into aux from table where something;
FOR row IN cursor_name LOOP
IF(aux > cursor_name%ROWCOUNT) THEN 'do something is not over';
ELSE 'do something else';
END IF;
END LOOP;
end;

Resources