Common table expression defining a function in a cursor - sql

Does somebody know if it is possible to use the a common table expression that defines a local function inside a PL/SQL cursor?
For example this code produce an error for me:
DECLARE
CURSOR LV_CUR IS
WITH function fct2(x number) return number is
begin
return 2*x;
end;
TEMP_AV AS
(
SELECT
fct2(LEVEL)
FROM
DUAL
CONNECT BY LEVEL < 10
)
SELECT
*
FROM
TEMP_AV;
BEGIN
FOR I IN LV_CUR
LOOP
NULL;
dbms_output.put_line(i.level);
END LOOP;
END;
The error is :
ORA-06550: Ligne 3, colonne 18 :
PL/SQL: ORA-00905: mot-clé absent
ORA-06550: Ligne 3, colonne 3 :
PL/SQL: SQL Statement ignored
ORA-06550: Ligne 6, colonne 5 :
PLS-00103: Encountered the symbol "END" when expecting one of the following:
begin function pragma procedure subtype type
current cursor delete
exists prior
I am able to execute the query (with the common table expression including a function definition) outside PL/SQL.

Yes and no. The 12.1 docs for the CURSOR statement explicitly say:
Restriction on select_statement
This select_statement cannot have a WITH clause.
This docs are wrong in this case, since you can have a WITH clause, it apparently just can't have the new 12c PL/SQL declarations in it. This block works fine, for example.
DECLARE
CURSOR LV_CUR IS
WITH
TEMP_AV AS
(
SELECT
level
FROM
DUAL
CONNECT BY LEVEL < 10
)
SELECT
*
FROM
TEMP_AV;
BEGIN
FOR I IN LV_CUR
LOOP
NULL;
dbms_output.put_line(i.level);
END LOOP;
END;
/

Related

How to fix strange oracle PL/SQL error - "PLS-00049: bad bind variable"

I am trying to write an execute an oracle PL/SQL function that return the number of records within a table, where one of the fields matches a certain name.
Here is my code:
create or replace function getNum
return number
as
v_x number;
begin
SELECT COUNT(*) INTO :v_x
FROM UserResponses WHERE NHSPlatform_Name = 'Improvement Data and Analytics';
return v_x;
end;
/
execute getNum();
I keep getting strange errors:
Function GETNUM compiled
LINE/COL ERROR
--------- ------------------------------------------------------------- 6/22 PLS-00049: bad bind variable 'V_X' Errors: check compiler log
Error starting at line : 85 in command - BEGIN getNum(); END; Error
report - ORA-06550: line 1, column 7: PLS-00905: object
B7011343.GETNUM is invalid ORA-06550: line 1, column 7: PL/SQL:
Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
First thing is to correct your function as pmdba wrote in his answer.
This would then be a correct function:
create or replace function getNum
return number
as
v_x number;
begin
SELECT COUNT(*) INTO v_x
FROM UserResponses WHERE NHSPlatform_Name = 'Improvement Data and Analytics';
return v_x;
end;
/
Then you need to call your function in correct way. This is one way of calling it:
select getNum from dual;
here is a small demo
Drop the ":" from the select statement:
create or replace function getNum
return number
as
v_x number;
begin
SELECT COUNT(*) INTO v_x
FROM UserResponses WHERE NHSPlatform_Name = 'Improvement Data and Analytics';
return v_x;
end;
/
select getNum() from dual;
Try this:
SQL> VARIABLE ret_val NUMBER;
SQL> execute :ret_val := getnum;
PL/SQL procedure successfully completed.
SQL> select :ret_val from dual;

How do I convert this procedure to a PLSQL block correctly ? Reference to uninitialized function

The following block compiles correctly .(Unimportant parts redacted)
CREATE OR REPLACE PROCEDURE testProc
IS
TYPE test_h IS TABLE OF VARCHAR2(100) INDEX BY VARCHAR2(100);
test_h_list test_h;
TYPE l_ids IS TABLE OF VARCHAR(100);
l_id_list l_ids;
BEGIN
test_h_list('A'):='Apple';
test:=test_h_list.FIRST;
WHILE test IS NOT NULL LOOP
BEGIN
SELECT tbl1.l_id BULK COLLECT INTO l_id_list
WHERE ....
....
....
END;
However, when I attempt to convert it into a plsql block
DECLARE
TYPE test_h IS TABLE OF VARCHAR2(100) INDEX BY VARCHAR2(100);
test_h_list test_h; --ORA-06531: Reference to uninitialized collection :-(
TYPE l_ids IS TABLE OF VARCHAR(100);
l_id_list l_ids;
BEGIN
test_h_list('A'):='Apple';
test:=test_h_list.FIRST;
WHILE test IS NOT NULL LOOP
BEGIN
SELECT tbl1.l_id BULK COLLECT INTO l_id_list
WHERE ....
....
....
END;
I get the 'ORA-06531: Reference to uninitialized collection ' error as annotated above. I tried searching around and came across this and based on the examples here
I came up with this
DECLARE
TYPE test_h IS TABLE OF VARCHAR2(100) INDEX BY VARCHAR2(100);
test_h_list test_h := test_h();
TYPE l_ids IS TABLE OF VARCHAR(100);
l_id_list l_ids :=l_ids();
BEGIN
test_h_list.EXTEND(100);
l_ids.EXTEND(100);
test_h_list('A'):='Apple';
test:=test_h_list.FIRST;
WHILE test IS NOT NULL LOOP
BEGIN
SELECT tbl1.l_id BULK COLLECT INTO l_id_list
WHERE ....
....
....
END;
But this throws an error saying PLS-00222: no function with name 'test_h' exists in this scope. Any idea on what I might be missing?
MCVE
Script -
DECLARE
TYPE test_h IS TABLE OF VARCHAR2(100) INDEX BY VARCHAR2(100);
test_h_list test_h := test_h(); --Line 3
TYPE l_ids IS TABLE OF VARCHAR(100);
l_id_list l_ids := l_ids();
test_str VARCHAR(50);
BEGIN
test_h_list.EXTEND(100);
l_id_list.EXTEND(100);
test_h_list('App'):='1';
test_h_list('Red'):='2';
test_str:=test_h_list.FIRST;
WHILE test_str IS NOT NULL LOOP
BEGIN
SELECT TABLE1.DEPT BULK COLLECT INTO l_id_list
FROM TABLE1
WHERE TABLE1.NAME = test_str;
FOR indx IN 1..l_id_list.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE( l_id_list(indx));
END LOOP;
test_str:=test_h_list.NEXT(test_str);
EXCEPTION
WHEN OTHERS THEN -- Just print the failure to logs
NULL;
END;
END LOOP;
END;
/
Error Report -
Error starting at line 1 in command:
DECLARE
TYPE test_h IS TABLE OF VARCHAR2(100) INDEX BY VARCHAR2(100);
test_h_list test_h := test_h();
TYPE l_ids IS TABLE OF VARCHAR(100);
l_id_list l_ids := l_ids();
test_str VARCHAR(50);
BEGIN
test_h_list.EXTEND(100);
l_id_list.EXTEND(100);
test_h_list('App'):='1';
test_h_list('Red'):='2';
test_str:=test_h_list.FIRST;
WHILE test_str IS NOT NULL LOOP
BEGIN
SELECT TABLE1.DEPT BULK COLLECT INTO l_id_list
FROM TABLE1
WHERE TABLE1.NAME = test_str;
FOR indx IN 1..l_id_list.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE( l_id_list(indx));
END LOOP;
test_str:=test_h_list.NEXT(test_str);
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
END LOOP;
END;
Error report:
ORA-06550: line 3, column 23:
PLS-00222: no function with name 'TEST_H' exists in this scope
ORA-06550: line 3, column 13:
PL/SQL: Item ignored
ORA-06550: line 9, column 3:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 9, column 3:
PL/SQL: Statement ignored
ORA-06550: line 11, column 3:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 11, column 3:
PL/SQL: Statement ignored
ORA-06550: line 12, column 3:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 12, column 3:
PL/SQL: Statement ignored
ORA-06550: line 13, column 13:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 13, column 3:
PL/SQL: Statement ignored
ORA-06550: line 27, column 15:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
ORA-06550: line 27, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
Elapsed: 00:00:00.011
In your MCVE you're mixing up different types of PL/SQL table. Your test_h types is indexed so doesn't need to be initialized and can't be extended - since it's a sparse table type. So removing the := test_h() and the extend line makes this work:
DECLARE
TYPE test_h IS TABLE OF VARCHAR2(100) INDEX BY VARCHAR2(100);
test_h_list test_h; -- do no instantiate := test_h(); --Line 3
TYPE l_ids IS TABLE OF VARCHAR(100);
l_id_list l_ids := l_ids();
test_str VARCHAR(50);
BEGIN
-- test_h_list.EXTEND(100); -- do not extend either
l_id_list.EXTEND(100);
test_h_list('App'):='1';
test_h_list('Red'):='2';
test_str:=test_h_list.FIRST;
WHILE test_str IS NOT NULL LOOP
BEGIN
SELECT TABLE1.DEPT BULK COLLECT INTO l_id_list
FROM TABLE1
WHERE TABLE1.NAME = test_str;
FOR indx IN 1..l_id_list.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE( l_id_list(indx));
END LOOP;
test_str:=test_h_list.NEXT(test_str);
EXCEPTION
WHEN OTHERS THEN -- Just print the failure to logs
NULL;
END;
END LOOP;
END;
/
PL/SQL procedure successfully completed.
Your original first anonymous block wasn't doing either of those things, and had the same sparse table type for test_h, so should not have been getting the ORA-06531. You would have seen it if you'd removed the INDEX BY VARCHAR2(100) from the type definition, but that isn't what you showed.
You could also have got it from trying to refer to elements of l_id_list without initialising that - but as presented in the question, it would always have been initalized implicitly by the bulk collect in the loop, even if the real table being queried was empty - you'd just have an empty PL/SQL table.
The code you originally showed doesn't throw the error; and the MCVE is doing something different, closer to your second anonymous block than the first one.

How to declare a function in a PLSQL block and call same in a select query?

I have a code like this-:
DECLARE
--BEGIN
V_DATE DATE;
FUNCTION GETDATE(NUM_DAYS NUMBER) RETURN DATE IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO LEAVE.GES_LEV_REQ_PURG_ERR VALUES (NUM_DAYS, NUM_DAYS, SYSDATE);
COMMIT;
RETURN(SYSDATE);
END GETDATE;
BEGIN
--V_DATE := GETDATE(1);
SELECT GETDATE(1) INTO V_DATE FROM DUAL;
DBMS_OUTPUT.PUT_LINE(V_DATE);
END;
/
But this is throwing below error-
ORA-06550: line 17, column 10:
PLS-00231: function 'GETDATE' may not be used in SQL
ORA-06550: line 17, column 10:
PL/SQL: ORA-00904: : invalid identifier
ORA-06550: line 17, column 3:
PL/SQL: SQL Statement ignored
While if I am calling that function without select query, it works fine.
Please help.
Nested sub programs can be used in the scope of the procedure where it has been created. Nested subprograms cannot be used in SQL since it is not directly available as DB object. Please be aware that there are two engines executing your PL/SQL block(SQL Engine & PL/SQL Engine). While executing your query with nested function, SQL engine tries to match the function name with the DB object. But in your case it is a subprogram inside a procedure. Hope I am understandable.
And as you said, you could perform DML inside a function and it could be used in SQL provided it is declared as PRAGMA AUTONOMOUS_TRANSACTION
There are Basically two thing which need to ask before we proceed for
any answer. 1) What is the exact use for this kind of call or block.
2) Why you are not going for FUCNTION creation first and then call it.
NOTE : [ Anyhow you cannot call a FUNCTION in SELECT statement if it
has DML operations involved in it. ]
I will try to illustrate the ay it should work. Let me know if it
helps
--Create Function
CREATE OR REPLACE FUNCTION GETDATE(
NUM_DAYS NUMBER)
RETURN DATE
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO LEAVE.GES_LEV_REQ_PURG_ERR VALUES
(NUM_DAYS, NUM_DAYS, SYSDATE
);
COMMIT;
RETURN(SYSDATE);
END GETDATE;
-- Call the function but not in a SQL statement.
set serveroutput on;
DECLARE
lv_date DATE;
BEGIN
lv_date:=GETDATE();
dbms_output.put_line(lv_date);
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 SQL Problems using Variables

Having some simple issues here but I've been up for so many hours having trouble focusing so hoping someone else with a fresh head can answer this easy question which I can't seem to find a good answer that works for me in SQL Developer
DECLARE
TEST123 NUMBER;
BEGIN
SELECT COUNT(APP_ID) INTO TEST123 FROM applicant_credit;
SELECT * FROM APPLICANT_CREDIT;
END;
When I run this which seems easy enough to me I am getting error:
Error report:
ORA-06550: line 5, column 3:
PLS-00428: an INTO clause is expected in this SELECT statement
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
Also I have a stored procedure I made and works when I send it hard coded numbers, but I need to pass it the count of records from the applicant_credit mentioned in the earlier query which is what I'm actually after. Would the execute stored procedure work the same in the above block as it does outside a block. So should this work??
DECLARE
TEST123 NUMBER;
BEGIN
SELECT COUNT(APP_ID) INTO TEST123 FROM applicant_credit;
EXECUTE UPDATE_DECISION(102, 1, 1, 1, 1, TEST123);
SELECT * FROM APPLICANT_CREDIT;
END;
When I run this I get:
Error report:
ORA-06550: line 5, column 11:
PLS-00103: Encountered the symbol "UPDATE_DECISION" when expecting one of the following:
:= . ( # % ; immediate
The symbol ":=" was substituted for "UPDATE_DECISION" to continue.
Hitting the wall a bit here, any help is appreciated!!
for the first sql statement, you are getting that error because you cannot execute a select * statement inside a plsql block in that way. You can either use a cursor or a bulk collect statement or any other way to "redirect" the output of select * into a collection of records.
DECLARE
TEST123 NUMBER;
cursor c_cur
is select col_1, col_2 from application_credit;
BEGIN
SELECT COUNT(APP_ID) INTO TEST123 FROM applicant_credit;
-- loop through the cursor if required
END;
your second sql statement gave an error because you cannot use an execute statement inside the plsql block (unless it is execute immediate which is completely different). So, you can modify the code as follows :-
DECLARE
TEST123 NUMBER;
cursor c_cur is
select col1, col2 from applicant_credit;
BEGIN
SELECT COUNT(APP_ID) INTO TEST123 FROM applicant_credit;
UPDATE_DECISION(102, 1, 1, 1, 1, TEST123);
-- loop through the cursor if required
END;
some helpful links :-
http://www.techonthenet.com/oracle/cursors/declare.php
http://www.oracle.com/technetwork/issue-archive/2008/08-mar/o28plsql-095155.html
You do not need PL/SQL at all for this unless you are trying to learn it. All can be done with SQL only.
DECLARE
TEST123 NUMBER:= 0;
BEGIN
UPDATE_DECISION(102, 1, 1, 1, 1, TEST123);
-- The outer query is not required, this is only to show you the correct syntax.
SELECT * INTO TEST123 -- you are selecting from applicant_credit
FROM
(
SELECT COUNT(app_id) FROM applicant_credit
);
END;
/