OPEN emp_refcur FOR {plsql variable} giving error - sql

I am novice in plsql development.
I have a problem opening cursor on plsql variable (which has my dynamic SQL query). I am on ORACLE 12 i believe.
Details below:
emp_refcur SYS_REFCURSOR; -- Cursor declaration
component_sql:= 'select * from emp'; -- Base SQL
-- dynamic sql based on business logic
IF SOME_CONDITION THEN
filters:= ' where DEP IN (''ABC'',''DEF'')';
ELSE
filters:= ' where age>50';
component_sql:= component_sql||filters; -- appending filter to my base sql
OPEN emp_refcur FOR component_sql; -- opening the cursor
LOOP
FETCH emp_refcur INTO result;
EXIT WHEN emp_refcur%NOTFOUND;
dbms_output.put_line(result);
END LOOP;
CLOSE emp_refcur;
Compiling this procedure shown error at line 'OPEN emp_refcur FOR component_sql' and error are
Error(90,9): PL/SQL: Statement ignored
Error(90,29): PLS-00382: expression is of wrong type
I do not want to use bind values like
'OPEN emp_refcur FOR p_query_string USING p_deptno, p_sal;'
I have tried multiple approaches
OPEN emp_refcur FOR ''||component_sql||''; -- This approach is not causing compilation error, but running the procedure resulting
ORA-00900: invalid SQL statement
AND
OPEN emp_refcur FOR 'select * from emp'||filters; -- This is resulting the error
ORA-00900: invalid SQL statement
Not sure what i am missing here. Please help.
NOTE: Please ignore if there are any SQL query syntax errors. Because the SQL which i am printing after appending the filters is executing fine and getting results when i run separately.

I tried with your code and it has no issues. PLS-00382: expression is of wrong type --> This is a syntax error. It has nothing to do with bind variables. What is the declaration for result? I don't see in the snippet.
PLS-00382: expression is of wrong type
Cause: An expression has the wrong datatype for the context in which it was found.
Action: Change the datatype of the expression. You might want to use datatype conversion functions.
Declare
emp_refcur SYS_REFCURSOR;
filters varchar2(100);
component_sql varchar2(100);
result varchar2(20);
Begin
component_sql:= 'select 1 from dual'; -- Base SQL
-- dynamic sql based on business logic
IF 1=1 THEN
filters:= ' UNION ALL SELECT 2 from dual';
ELSE
filters:= ' where age>50';
End if;
component_sql:= component_sql||filters; -- appending filter to my base sql
OPEN emp_refcur FOR component_sql ; -- opening the cursor
LOOP
FETCH emp_refcur INTO result;
EXIT WHEN emp_refcur%NOTFOUND;
dbms_output.put_line(result);
END LOOP;
CLOSE emp_refcur;
END;

Related

IBM plsql - cursor

I am new with PLSQL (IDM db2) and I am trying to create a procedure to find and delete some admin_tasks if they exist.
Explanation:
By running the bellow query1, I can find out the list of tasks in the scheduler:
SELECT * FROM SYSTOOLS.ADMIN_TASK_LIST;
To delete the task, I can run this query2:
call sysproc.admin_task_remove('TASK_NAME', null);
So I want to create a procedure to delete all tasks with the pattern "My_Task_*".
This should delete all the tasks with that pattern and leave the other existent tasks intact. The procedure should handle cases where the admin_tasks doesn't exist, or no admin_tasks were ever created, and should run without throwing any errors.
I have researched and found out that this can be made using cursors.
Can you help me to implement this?
EDIT:
I managed to find this solution:
BEGIN
FOR v1 AS c1 CURSOR FOR
SELECT NAME
FROM SYSTOOLS.ADMIN_TASK_LIST
WHERE NAME LIKE 'My\_task\_%' ESCAPE '\'
DO
call sysproc.admin_task_remove(NAME, null);
END FOR;
END
This seems to work except if the SYSTOOLS.ADMIN_TASK_LIST was not yet defined.
If it's not defined than I get this error when I run the query: If it is not defined I get this error
: [Code: -204, SQL State: 42704]
"SYSTOOLS.ADMIN_TASK_LIST" is an undefined name.. SQLCODE=-204,
SQLSTATE=42704, DRIVER=4.25.1301
So how can I bypass this error? DOing something like first checking if SYSTOOLS.ADMIN_TASK_LIST is defined, if its defined to the above query if not do nothing.
Presuming that your client tool uses '#' as a statement delimiter.
BEGIN
FOR L1 AS
SELECT NAME
FROM SYSTOOLS.ADMIN_TASK_LIST
WHERE NAME LIKE 'My\_task\_%' ESCAPE '\'
DO
CALL ADMIN_TASK_REMOVE (L1.NAME, NULL);
END FOR;
END
Update:
With error handling for non-existing table. The error handler just "eats" the error on non-existing table use.
You must use dynamic sql for that.
BEGIN
DECLARE SQLSTATE CHAR (5);
DECLARE L_NAME VARCHAR (128);
DECLARE C1 CURSOR FOR S1;
DECLARE EXIT HANDLER FOR SQLSTATE '42704' BEGIN END;
PREPARE S1 FROM
'
SELECT NAME
FROM SYSTOOLS.ADMIN_TASK_LIST
WHERE NAME LIKE ''My\_task\_%'' ESCAPE ''\''
';
OPEN C1;
L1:
LOOP
FETCH C1 INTO L_NAME;
IF SQLSTATE = '02000' THEN LEAVE L1; END IF;
CALL ADMIN_TASK_REMOVE (L_NAME, NULL);
END LOOP L1;
CLOSE C1;
END

Using 'open cursor' as 'execute immediate'

I am trying to use open cursor as a substitute for execute immediate because my SQL statement could return multiple records.
open cur1 for rule_sql;
loop
dbms_output.put_line(cur1.rule_id);
end loop;
close cur1;
It throws an error saying: "PLS-00487: Invalid reference to variable 'CUR1'"
Has anyone had similar issues? Any help is much appreciated :)
The cursor is just a pointer to a result set. To reference its contents you need to fetch it into a variable. Note that the variable must be a record type which matches the projection of the query. This may be hard if you're using dynamic SQL to implement a fluid set of columns.
Anyway, something like this:
declare
cur1 sys_refcursor;
Type cur_rec is record (
rule_id number,
rule_desc varchar2(32));
row1 cur_rec;
....
Begin
...
open cur1 for stmt;
for row1 in cur1 loop
Dbms_output.put_line(row1.rule_id);
End loop;
....
End;
"If I do not know the type of columns in the result then I cannot create a variable to capture the cursor values."
Life is more complicated when you don't know the projection of your query at compile time. You can't use Native Dynamic SQL anymore, you need to go full DBMS_SQL.
In 11g Oracle introduced the so-called Method 4 Dynamic SQL. This allows us to handle variable projections at the cost of a lot more code. Adrian Billington wrote an excellent introduction to this on his Oracle-developer.net site. Check it out
You have missed the fetch statement - see Example 7.4 in docs
open cur1 for rule_sql;
loop
fetch cur1 into my_row_variable;
exit when cur1%notfound;
dbms_output.put_line(cur1.rule_id);
end loop;
close cur1;

db2 dynamic sql with select into clause in anonymous block

I am working on a DB2 SQL code.
I need to get the count of records from a list of tables. The table details will be fetched from a cursor with select.
To get the count of records I have been trying with a SELECT INTO statement. Since the table names will be varying, I am using a dynamic SQL code.
I am sharing the piece of code that I have been trying.
I am not quite sure of the syntax while using DB2 SELECT INTO and Dynamic SQL combination. I am getting the following error with the below attempt.
Can anyone tell me why this is so? If possible, appreciate if you could share a working code of DB2 select into and dynamic sql.
SQL0104N An unexpected token "statmnt2" was found following "SET statmnt2 := 'set ? = (SELECT COD_TIPO_ARQU FROM '||indbn". Expected tokens may include: "".
DECLARE
indbnm VARCHAR(30);
intblnm VARCHAR(30);
v_errorText varchar2(50);
statmnt2 VARCHAR(1000);
VAR_COD_TIPO_ARQU CHAR(1);
stmt1 STATEMENT;
statmnt2 VARCCHAR2(100);
BEGIN
indbnm := "db2inst5";
intblnm:= "rules";
SET statmnt2 := 'set ? = (SELECT COD_TIPO_ARQU FROM '||indbnm||'.'||intblnm||' FETCH FIRST 1 ROWS ONLY)';
PREPARE stmt1 FROM statmnt2;
EXECUTE stmt1 into VAR_COD_TIPO_ARQU ;
DBMS_OUTPUT.PUT_LINE(VAR_COD_TIPO_ARQU);
EXCEPTION
WHEN OTHERS THEN
v_errorText :=SUBSTR(SQLERRM,1, 1024);
DBMS_OUTPUT.PUT_LINE('FAILED WITH MESSAGE: '||v_errorText);
END;

cant compile any cursor example

I'm trying to make any cursor example working, but just cant make it happen.
IDE: Oracle SQL developer
database: 10g sql oracle
I've tried this three examples, but it didn't compile well. Can someone provide me with working examples?
First one:
EDIT: works! just had 'where where'
DECLARE
CURSOR person_data_cur
as
SELECT *
FROM personal_data
WHERE where name='Karol';
BEGIN
FOR person_data
IN person_data_cur
LOOP
DBMS_OUTPUT.put_line (
person_data.surname);
END LOOP;
END;
and this:
DECLARE my_cursor1 CURSOR FOR
SELECT name, surname
FROM personal_data
WHERE name='Karol';
^error:
Error report -
ORA-06550: row 1, column 27:
PLS-00103: found symbol "FOR" when expected one of following:
:= . ( # % ; not null range default character
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
or even this:
EDIT: this one is OK, just my IDE ignored first two lines, and executed others... but i guess it was OK since it wasnt inside any block or procedure
CURSOR c1
IS
SELECT course_number
FROM courses_tbl
WHERE course_name = name_in;
For Second Example : Your syntax is incorrect.
The pseudo code for the cursor is :
Declare
Cursor Cursor_name
is
select * from table where <Conditions>;
Begin
For cur_Rec in cursor_name
Loop
<what you want to do>
End Loop;
End;
For the third example, I believe name_in is an input parameter, please correct the syntax to :
CURSOR c1 (name_in VARCHAR2)
IS
SELECT course_number
FROM courses_tbl
WHERE course_name = name_in;
Please refer Cursor Declaration for more details

Oracle: Find the position of an error in dynamic SQL using SQL or PL/SQL

How can I find the position of an error in a Dynamic SQL statement in PL/SQL or SQL?
From SQL*Plus I see the position of an error in, for example, an invalid SQL DML statement:
SYS#orcl> SELECT
2 X
3 FROM
4 TABLEX
5 /
TABLEX
*
ERROR at line 4:
ORA-00942: table or view does not exist
SQL*Plus shows the error with the line number, and prints and marks that line with an asterisk where the error is found.
Converting to Dynamic SQL, I can get the error code (SQLCODE) and error message (SQLERRM):
SYS#orcl> SET SERVEROUTPUT ON
SYS#orcl> BEGIN
2 EXECUTE IMMEDIATE 'SELECT X FROM TABLEX';
3 EXCEPTION
4 WHEN OTHERS THEN
5 DBMS_OUTPUT.PUT_LINE('SQLCODE:' || SQLCODE);
6 DBMS_OUTPUT.PUT_LINE('SQLERRM:' || SQLERRM);
7 END;
8 /
SQLCODE:-942
SQLERRM:ORA-00942: table or view does not exist
But how do I get the position of the error in the Dynamic SQL string?
I see that Oracle provides a SQL Communications Area (SQLCA) that contains interesting information about an error. In particular:
the SQLCODE and SQLERRM fields (that might be the source of the data retrieved with the respective PL/SQL functions),
the SQLERRD field where the SQLERRD(5) element that gives the 'parse error offset'.
Is it possible to access SQLERRD from PL/SQL or SQL? If so, how? If not, what other technique can give the location of the error from PL/SQL or SQL?
(Here http://docs.oracle.com/cd/B28359_01/appdev.111/b31231/chapter8.htm#BABIGBFF the SQLCA is documented and accessed with Pro*C.)
(The answer here how to declare SQLCA.SQLERRD? seems to indicate that SQLERRD is not defined in PL/SQL and therefore not accessible.)
(The discussion here Why doesn't Oracle tell you WHICH table or view does not exist? gives some suggestions to show bad SQL using trace files and to show the location of errors in some development tools.)
you got a package for extracting error messages in dbms_utility
begin
.. generate error
exception when others then
dbms_output.put_line(
dbms_utility.format_call_stack() || chr(10) ||
dbms_utility.format_error_backtrace() || chr(10) ||
dbms_utility.format_error_stack())
end;
Running the statement through dynamic PL/SQL will store the relevant line number in the error stack.
For example, this statement has an error on line 4:
declare
v_count number;
v_bad_sql varchar2(32767) :=
'SELECT
X
FROM
TABLEX';
begin
execute immediate v_bad_sql into v_count;
exception when others then
begin
execute immediate
'begin for i in ( '||v_bad_sql||') loop null; end loop; end;';
exception when others then
dbms_output.put_line(sqlerrm);
end;
end;
/
ORA-06550: line 4, column 4:
PL/SQL: ORA-00942: table or view does not exist
ORA-00942: table or view does not exist
ORA-06550: line 1, column 18:
PL/SQL: SQL Statement ignored
ORA-00942: table or view does not exist
There are some drawbacks to this method:
It requires some extra, ugly code to catch the exception and re-try the SQL.
The example only works for selects. You'll need to tweak it for insert, update, delelete, merge, dynamic PL/SQL, etc. Normally you should know what kind of SQL statement it is. If you're unlucky, you'll need to parse the statement, which can be very difficult.
The column number is wrong if the entire PL/SQL statement is on one line.