Oracle Cursor, No Data Found exception - oracle

I'm trying to modify a procedure that removes a user from the system, adding a bit that generates a list of any areas over which they are the only admin. I have read that cursors can work when 0 results are found, so I am assuming that my query is the problem. Tables 1 (A) and 2 (B&C) are joined by the primary key of table A. Subquery B is a list of all the areas a user is admin over, and Subquery C is a list of all areas with only one admin. In the specific instance I'm testing, there is no matching area_id between subqueries B and C. Is there a way I can modify this query to work properly? I tried moving the opening of the cursor behind an if statement, but still receive the error, so I'm assuming it's the declaration of the cursor that is resulting in the exception being thrown.
CURSOR AA_CUR IS
SELECT AREA_NAME
FROM FILE_TRANSFER.AREA_LU A,
(SELECT AREA_ID
FROM FILE_TRANSFER.USER_AREA_ACCESS
WHERE AREA_ADMIN='Y'
AND USERNAME IN (SELECT USERNAME
FROM FILE_TRANSFER.USER_INFORMATION
WHERE USER_INFORMATION_ID = v_UIID)) B,
(SELECT AREA_ID, AREA_ADMIN, COUNT(*)
FROM FILE_TRANSFER.USER_AREA_ACCESS
WHERE AREA_ADMIN='Y'
HAVING COUNT(*) = 1
GROUP BY AREA_ID, AREA_ADMIN) C
WHERE A.AREA_ID = B.AREA_ID
AND B.AREA_ID = C.AREA_ID;

Your assumption is wrong.
Cursor's SELECT can't return NO_DATA_FOUND. If it returns nothing, it is silently ignored, nothing happens.
SQL> declare
2 cursor c1 is
3 select 'x' from dual
4 where 1 = 2; -- this query returns "nothing"
5 c1r c1%rowtype;
6 begin
7 open c1;
8 fetch c1 into c1r;
9 close c1;
10 end;
11 /
PL/SQL procedure successfully completed.
See? Nothing happened. Just to show that query will raise NO_DATA_FOUND (if ran separately):
SQL> declare
2 l_val varchar2(1);
3 begin
4 select 'x' into l_val from dual
5 where 1 = 2; -- this will raise NO_DATA_FOUND
6 end;
7 /
declare
*
ERROR at line 1:
ORA-01403: no data found
ORA-06512: at line 4
SQL>
Therefore, there must be some other SELECT statement that is raising that error. Which one? Can't tell as you didn't post much of your code and - even if you did - without your tables and data it would be difficult to guess.
So, what to do? Debug your code. A simple option is to put DBMS_OUTPUT.PUT_LINE calls between every two statements so that you'd see which part of code actually failed - then you'll be able to try to fix it.

Related

PL/SQL IF statement and variable declaration

I am trying to make the code posted below work, but I am receiving an error about the IF statement:
DECLARE
a number;
BEGIN
SELECT Pers_API.Is_Manager_(10018) INTO a from dual;
EXCEPTION
WHEN no_data_found THEN a := 0;
END;
IF (a >0) THEN
SELECT 1 from dual;
ELSE
SELECT 0 from dual;
END IF;
In the first block I am declaring an 'A' variable and setting it to 0 (by the way, the API call result will be 0 or 1, nothing else). The first block works fine or - at least - I do think so. But the IF block is not working and having this error:
PLS-00103:Encountered the symbol "IF"
Any help is appreciated.
Update: I have tried it this way:
DECLARE
a number:=0;
BEGIN
SELECT Pers_API.Is_Manager_(10018) INTO a from dual;
IF (:a >0) THEN
SELECT 1 from dual;
ELSE
SELECT 0 from dual;
END IF;
END;
I received the exception below:
ORA-01008: not all variables bound
Update
In other words, what I am trying to do is:
if Pers_API.Is_Manager_(10018) returns true (as 1), I have to do another select statement
if it's false (as 0), I have to return null.
Any other ideas are appreciated.
A few objections, if I may.
there's no need to select from dual in order to assign a value to a variable, when that value is returned by a function - simply assign it
exception handler seems to be superfluous. You said that the is_manager_ function returns 0 or 1 and nothing else. Therefore, it'll always return something, so - why do you expect that SELECT to return NO_DATA_FOUND? Besides, such a case should be handled by the function itself (i.e. you have to make sure that it returns 0 or 1 and nothing else)
the last paragraph you wrote is somewhat strange. You said that - if the function returns 1, you have to run another SELECT statement. Otherwise, you have to return null (while code you wrote, select 0 ... suggests a zero (0), not null); so, which one is true?
also, saying that you have to return something (null, 0, whatever) suggests that piece of code you wrote should be a function that returns a value. Is that so?
Code that actually compiles might look like in this example.
First, a function (based on Scott's schema) that checks whether an employee is a manager:
SQL> create or replace function is_manager_ (par_empno in number)
2 return number
3 is
4 /* function checks whether PAR_EMPNO belongs to a manager and returns:
5 - 1 - employee is a manager
6 - 0 - employee is NOT a manager
7 */
8 retval number := 0;
9 begin
10 select max(1)
11 into retval
12 from emp e
13 where mgr = par_empno;
14
15 return retval;
16 exception
17 when no_data_found then
18 return retval;
19 end;
20 /
Function created.
SQL>
Code you posted, rewritten so that it does something:
if an employee is a manager, returns his/her salary
otherwise, it does not
SQL> declare
2 a number := is_manager_(&&l_empno);
3 l_sal emp.sal%type;
4 begin
5 if a > 0 then
6 select sal
7 into l_sal
8 from emp
9 where empno = &&l_empno;
10 dbms_output.put_line('Employee is a manager and his/her salary is ' || l_sal);
11 else
12 null;
13 dbms_output.put_line('Employee is not a manager');
14 end if;
15 end;
16 /
Enter value for l_empno: 7698
Employee is a manager and his/her salary is 2850
PL/SQL procedure successfully completed.
SQL> undefine l_empno
SQL> /
Enter value for l_empno: 7369
Employee is not a manager
PL/SQL procedure successfully completed.
SQL> undefine l_empno
SQL> /
Enter value for l_empno: -1
Employee is not a manager
PL/SQL procedure successfully completed.
SQL>
See if you can adjust it so that it fits your needs. If not, do answer to my objections posted at the beginning of this message, and we'll see what to do next.
It appears to me that what you are really trying to achieve here is to run a query which must use the result obtained from the package function as a condition. If so, the PL/SQL block wouldn't be needed and can be written as a basic select operation.
What is still not clear from your explanation is whether your select query
SELECT 1 from dual is the actual query you are about to use in your code.Generally, in the select statement, we use CASE expression to evaluate the conditional logic, which essentially does what IF ELSE condition was supposed to do to return the desired result.The query would have a structure of this form,
SELECT
CASE
WHEN pers_api.is_manager_(10018) = 1 THEN some_expression
ELSE some_other_expression --If you omit the else condition, it defaults to null when all others are false
END
END
as
FROM DUAL;
More specifically, if you want to have the resultant expression come from another database table, the queries can take the form
SELECT
CASE
WHEN pers_api.is_manager_(10018) = 1 THEN some_expression
ELSE some_other_expression --If you omit the else condition, it defaults to null when all others are false
END
as
FROM table_to_run_query
WHERE where_clauses = some_values;
If you say that your resultant expression from the table involves many other constructs, the challenge would be to compose it in a single query cleverly to avoid any PL/SQL at all. That would be achievable, but it will require that you explain us with some sample data and expected result, as to what exactly you want,preferably in a new question.
It would not be complete if I don't mention the REFCURSOR technique to obtain results from a select query in PL/SQL. It is either through DBMS_SQL.RETURN_RESULT(Oracle 12c and above) or using a REFCURSOR bind variable running it in SQL* Plus or as Script in other tools (F5).
VARIABLE x refcursor;
DECLARE BEGIN
IF
pers_api.is_manager_(10018) = 1
THEN
OPEN :x FOR SELECT some_columns FROM some_tables;
ELSE
OPEN :x FOR SELECT some_columns FROM other_tables;
END IF;
--DBMS_SQL.RETURN_RESULT(:x); --12c and above
END;
/
PRINT x -- 11g

Using cursor after a delete statement in a stored procedure [duplicate]

This question already has an answer here:
Closed 10 years ago.
Possible Duplicate:
Oracle Accessing updated records during the same transaction
I have an Oracle stored procedure somewhat like this (actual sqls are different)
CREATE OR REPLACE PROCEDURE mysp
IS
v_copied_row table%ROWTYPE ;
CURSOR p_copied_rows IS
select *
from table ;
BEGIN
delete from table
where <some condition>
OPEN p_copied_rows ;
LOOP
FETCH p_copied_rows into v_copied_rows ;
<do something with fetched row>
END LOOP
close p_copied_rows;
END;
Ideally, I am expecting that the deleted rows should not be part of the result set I am fetching from the cursor, but those are.
I am new to oracle, and want to understand if i am doing something wrong here?
P.S. I've to use cursor for some complex things, so replacing cursor with the SQL is not an option.
If your actual code matched the code you posted, the rows you you delete would not be returned by the cursor
If I create a table with 100 rows
SQL> ed
Wrote file afiedt.buf
1 create table foo
2 as
3 select level col1
4 from dual
5* connect by level <= 100
SQL> /
Table created.
and then create a PL/SQL block that replicates what you posted which deletes 98 of the rows, the cursor that is opened will return only 2 rows
SQL> select count(*) from foo;
COUNT(*)
----------
100
SQL> declare
2 cursor non_deleted_rows
3 is select *
4 from foo;
5 l_rec foo%rowtype;
6 begin
7 delete from foo
8 where col1 <= 98;
9
10 open non_deleted_rows;
11 loop
12 fetch non_deleted_rows into l_rec;
13 exit when non_deleted_rows%notfound;
14
15 dbms_output.put_line( l_rec.col1 );
16 end loop;
17 end;
18 /
99
100
PL/SQL procedure successfully completed.
Now, if you open the cursor before you issue the DELETE, the cursor will return the rows that were deleted. Perhaps in your actual code, the OPEN statement is before the DELETE.

Declare cursor in sqlplus command mode

I have an SQL file which uses declares a cursor and I am running it using #abc, but it did not execute all statements and waiting without returning to command prompt. It did not proceed after declare cursor statement. When I tried to run the declare cursor statement in command mode, the same problem is happening again. I am able to return to SQL priompt only after pressing Ctrl + C. I am very new to SQL world. Though this could be a basic mistake, I am not able to find out the solution in any site. Any help is greatly appreciated.
SQL> DECLARE CURSOR id_cursor IS SELECT id FROM user_names WHERE dept_no = 1002
AND BITAND(flags, 4) = 4 AND time_created BETWEEN 1137974400 AND 1326067199;
2
3
4 ;
5
6
All DECLARE and BEGIN blocks in SQL*Plus need to be ended with a / on a new empty line:
SQL> DECLARE
2 CURSOR c IS SELECT 1 FROM DUAL;
3 BEGIN
4 NULL;
5 END;
6 /
PL/SQL procedure successfully completed.
Without this / SQL*Plus has no way to know that your statement has ended (so in your example it waits for user input).
After the ; type a / and enter. This will run your PL/SQL statement. But the sample you've given does nothing but declare a cursor. You must then use it like so:
declare
cursor ID_CURSOR is
select ID
from USER_NAMES
where DEPT_NO = 1002
and bitand(FLAGS, 4) = 4
and TIME_CREATED between 1137974400 and 1326067199;
begin
for REC in ID_CURSOL loop
<do something with your data>;
end loop;
end;
/

simple way to return 2 or more rows using a SELECT in pl/sql

I am trying to figure out the most simple way to return 2 or more rows using a select statement in pl/sql to the sqlplus output. Here is a basic example.
begin
select sysdate,'12345' xid from dual
union all
select sysdate,'67890' xid from dual;
end;
/
Error is "an INTO clause is expected in this SELECT statement"
You would use a cursor to return the result of a query:
SQL> VARIABLE x REFCURSOR
SQL> BEGIN
2 OPEN :x FOR SELECT ROWNUM FROM dual CONNECT BY LEVEL <= 3;
3 END;
4 /
PL/SQL procedure successfully completed.
SQL> PRINT x
ROWNUM
----------
1
2
3
Yes, you can return a ResultSet from a PL/SQL. Take a look at this URL.
When writing a SQL code in a BEGIN-END block, you cannot display its result-set in the editor. Trim the BEGIN-END from your query, and it would happily display the results.
Basically, code within a BEGIN-END block is not used for displaying results, it is used for data processing instead. So you should simply write:
SELECT SYSDATE,'12345' XID FROM DUAL
UNION ALL
SELECT SYSDATE,'67890' FROM DUAL;
to display the result-set in your SQL editor.
Also notice that I removed alias "XID" in second SELECT statement: you need to give column aliases in only first query in a UNION/UNION ALL statement.

Reasonable SELECT ... INTO Oracle solution for case of multiple OR no rows

I just want to SELECT values into variables from inside a procedure.
SELECT blah1,blah2 INTO var1_,var2_
FROM ...
Sometimes a large complex query will have no rows sometimes it will have more than one -- both cases lead to exceptions. I would love to replace the exception behavior with implicit behavior similiar to:
No rows = no value change, Multiple rows = use last
I can constrain the result set easily enough for the "multiple rows" case but "no rows" is much more difficult for situations where you can't use an aggregate function in the SELECT.
Is there any special workarounds or suggestions? Looking to avoid significantly rewriting queries or executing twice to get a rowcount before executing SELECT INTO.
Whats wrong with using an exception block?
create or replace
procedure p(v_job VARCHAR2) IS
v_ename VARCHAR2(255);
begin
select ename into v_ename
from (
select ename
from scott.emp
where job = v_job
order by v_ename desc )
where rownum = 1;
DBMS_OUTPUT.PUT_LINE('Found Rows Logic Here -> Found ' || v_ename);
EXCEPTION WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No Rows found logic here');
end;
SQL> begin
p('FOO');
p('CLERK');
end; 2 3 4
5 /
No Rows found logic here
Found Rows Logic Here -> Found SMITH
PL/SQL procedure successfully completed.
SQL>
You could use a for loop. A for loop would do nothing for no rows returned and would be applied to every row returned if there where multiples. You could adjust your select so that it only returns the last row.
begin
for ARow in (select *
from tableA ta
Where ta.value = ???) loop
-- do something to ARow
end loop;
end;

Resources