Error in Oracle (PLS-00103: Encountered the symbol "="...) trying to select into a variable for use in a cursor - sql

I'm creating a procedure. I'm selecting a max date from a table into a var, and then intending to use that var in sql for a cursor. It looks like:
l_max_update_date DATE;
--Param var
l_max_update_date := NULL;
SELECT max(pivlog.last_update_date) as maxdate into l_max_update_date
FROM piv_verification_log pivlog;
...and then...
--No data in log table? Create a max_update_date
IF l_max_update_date IS NULL
THEN
l_max_update_date := TO_TIMESTAMP('2014/SEP/1 00:00:00', 'YYYY/MON/DD HH24:MI:SS');
END IF;
Finally...
--Get affected employees (those who have certified since the max(last_update_date) listed in the log table)
CURSOR affected_employees_cursor
IS
SELECT [columns]
FROM [tables]
WHERE LAST_UPDATE_DATE > l_max_update_date;
But, whenever I compile, I get this error message:
[Error] PLS-00103 (47: 22): PLS-00103: Encountered the symbol "=" when expecting one of the following:
constant exception
table long double ref
char time timestamp
which points at this line:
l_max_update_date := NULL;
I appreciate your insight. I'm thinking it has to do with the order or location in the procedure where I'm defining the var and cursor(?).
Thank you.

You can't have executable code before the first BEGIN. It would help if you'd post all the code for your procedure, but given what can be seen above it looks to me like your procedure should be something like:
CREATE OR REPLACE PROCEDURE SOME_PROC AS
l_max_update_date DATE := NULL; -- not really needed - variables are
-- initialized to NULL if no other
-- initial value is given.
CURSOR affected_employees_cursor IS
SELECT [columns]
FROM [tables]
WHERE LAST_UPDATE_DATE > l_max_update_date;
rowAffected_employee affected_employees_cursor%ROWTYPE;
BEGIN
BEGIN
SELECT max(pivlog.last_update_date) as maxdate
into l_max_update_date
FROM piv_verification_log pivlog;
EXCEPTION
WHEN NO_DATA_FOUND THEN
l_max_update_date := NULL;
END;
--No data in log table? Create a max_update_date
IF l_max_update_date IS NULL THEN
l_max_update_date := TO_DATE('2014/SEP/1 00:00:00', 'YYYY/MON/DD HH24:MI:SS');
END IF;
OPEN affected_employees_cursor;
LOOP
FETCH affected_employees_cursor
INTO rowAffected_employee;
EXIT WHEN affected_employees_cursor%NOTFOUND;
-- do something useful with the data fetched from the cursor
END LOOP;
CLOSE affected_employees_cursor;
END SOME_PROC;
Share and enjoy.

Further to Bob's answer, the cursor will use whatever value l_max_update_date has at the point the cursor is opened, so it doesn't have to be set before the cursor is declared.
If you'd prefer that to be more obvious in your code then you could also pass the date to the cursor as a parameter:
CURSOR affected_employees_cursor (p_last_update_date DATE) IS
SELECT [columns]
FROM [tables]
WHERE LAST_UPDATE_DATE > p_max_update_date;
and then call it with:
OPEN affected_employees_cursor (l_max_update_date);
Or you could combine the lookup-up into the cursor definition, as long as you only open it once, and skip the separate look-up and check:
CREATE OR REPLACE PROCEDURE SOME_PROC AS
CURSOR affected_employees_cursor IS
SELECT [columns]
FROM [tables]
WHERE LAST_UPDATE_DATE > (
SELECT COALESCE(MAX(pivlog.last_update_date), DATE '2014-09-01')
FROM piv_verification_log pivlog
);
rowAffected_employee affected_employees_cursor%ROWTYPE;
BEGIN
OPEN affected_employees_cursor;
LOOP
FETCH affected_employees_cursor
INTO rowAffected_employee;
EXIT WHEN affected_employees_cursor%NOTFOUND;
-- do something useful with the data fetched from the cursor
END LOOP;
CLOSE affected_employees_cursor;
END SOME_PROC;
/
Or even simpler use an implicit cursor loop:
CREATE OR REPLACE PROCEDURE SOME_PROC AS
BEGIN
FOR rowAffected_employee In (
SELECT [columns]
FROM [tables]
WHERE LAST_UPDATE_DATE > (
SELECT COALESCE(MAX(pivlog.last_update_date), DATE '2014-09-01')
FROM piv_verification_log pivlog
)
)
LOOP
-- do something useful with the data fetched from the cursor
END LOOP;
END SOME_PROC;
/
Of course, depending on what you're doing with the data fetched form the cursor, this might be something that doesn't need PL/SQL at all and could be done in plain SQL.

Related

How to execute results of dbms_output.put_line

There is a table contains this kind of data: select to_char(sysdate,'day') from dual in a column. I want to get results of the every query that the table keeps.
My result set should be the result of select to_char(sysdate,'day') from dual query. So in this case it is a tuesday.
SO_SQL_BODY is Varchar2.
I wrote this code but it returns only table data.
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
END LOOP;
END a_proc;
DECLARE
res varchar2(4000);
sql_str varchar2(1000);
BEGIN
FOR r IN
(select SO_SQL_BODY FROM SO_SUB_VARS WHERE SO_SQL_BODY IS NOT NULL
)
LOOP
sql_str := r.SO_SQL_BODY;
EXECUTE immediate sql_str INTO res;
dbms_output.put_line(sql_str);
dbms_output.put_line('***********************');
dbms_output.put_line(res);
dbms_output.put_line('***********************');
END LOOP;
END;
/
Try this - iterate to not null records - execute them and print the result.This script works supposing the fact that SO_SQL_BODY contains a query which projects only one column.Also if the projection is with more than two columns then try to use a refcursor and dbms_sql package
İf var_names(indx).SO_SQL_BODY output is a runnable sql text;
CREATE or replace PROCEDURE a_proc
AS
CURSOR var_cur IS
select SO_SQL_BODY FROM SO_SUB_VARS group by SO_SQL_BODY;
var_t var_cur%ROWTYPE;
TYPE var_ntt IS TABLE OF var_t%TYPE;
var_names var_ntt;
BEGIN
OPEN var_cur;
FETCH var_cur BULK COLLECT INTO var_names;
CLOSE var_cur;
FOR indx IN 1..var_names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(var_names(indx).SO_SQL_BODY);
EXECUTE IMMEDIATE var_names(indx).SO_SQL_BODY;
END LOOP;
END a_proc;
You don't need a full cursor for this example. An implicit one would make it a lot shorter.
create or replace procedure a_proc is
lReturnValue varchar2(250);
begin
for q in (select so_sql_body from so_sub_vars group by so_sql_body)
loop
execute immediate q.so_sql_body into lReturnValue;
dbms_output.put_line(lReturnValue);
end loop;
end a_proc;
You should add an exception handler that will care for cases where there is a bad SQL query in your table. Also note that executing querys saved in a database table is your entry point to SQL injection.

How to populate variable in pl sql on declaration in store procedure with sql statement return

So I have a variable on the declaration on the store procedure. It must have a default value that is populated on the return of a sql statement. The thing is that that variable is used in the declaration of the query of a cursor, thats why it needs to be populated on the declaration. How am i supposed to do this?
create or replace
PROCEDURE PROCESAR AS
ultimaejecucion date := select fecha from table where rownum<=1;
--I know I have to use the word INTO but throws me error when i compile.
cursor cursor_licencias is
select lic.campo1
from lic lic
where lic.licfhing >= ultimaejecucion
BEGIN
open cursor
.
.
.
END PROCESAR;
Your problem is quite unclear. If your cursor always depends on values from another table, you should include this table to query, as Multisync advised. If your cursor also has to depend on different rows of that table, you can use cursor parameters to choose that row:
create or replace
PROCEDURE PROCESAR AS
cursor cursor_licencias (cursor_parameter number default 1234) is
select lic.campo1
from lic lic
where lic.licfhing >= (select fecha from table where column = cursor_parameter);
BEGIN
open cursor cursor_licencias; -- using with default value
open cursor cursor_licencias (5678); -- using with value '5678'
END PROCESAR;
Example in oracle documentation.
You may move it into the cursor declaration:
create or replace
PROCEDURE PROCESAR AS
cursor cursor_licencias is
select lic.campo1
from lic lic
where lic.licfhing >= (select fecha from table where rownum<=1);
BEGIN
open cursor
.
.
.
END PROCESAR;
if you need this field somewhere else you can assign the value to the ultimaejecucion right after BEGIN
I would do something like:
declare
l_date date;
-- the value of l_date does not matter at this point
cursor sel_cur is
select 'It works' as val
from dual
where sysdate > l_date;
begin
-- populate l_date using query
select sysdate - 1
into l_date
from dual;
-- open cursor (implicitly).
-- here the value of l_date is used in the execution of the query
for rec in sel_cur
loop
dbms_output.put_line(rec.val);
end loop;
end;

PL/SQL cursor with IF condition

I Have below cursor in the code.
CURSOR cur1
IS
SELECT a, b, c, d,
FROM EMP;
BEGIN
--Stored procedure logic
END
This curosr is getting information from EMP table.
But I need to change is as per below
There is a table (Table1) with Key Value pairs.
If the Table1 value is TRUE then the cursor should be created with STUDENT table
If the table1 value is FALSE then the cursor should be created with EMP table.
I can check the Value in the Table1 as below
select t.value into variable1 from Table1 t where s.key='xxxxx';
And I want write something like
IF variable1 := 'true'
curosr created with STUDENT
ELSE
curosr created with EMP
END IF
BEGIN
--Stored procedure logic
END
How to do it?
In another way you can just keep two CURSORS for those two scenarios and OPEN them on the condition. Declaring two CURSORS will not affect to the performance; you should be careful when OPEN a CURSOR and FETCHING from it.
PROCEDURE Get_Details_On_Condition ( name_ OUT VARCHAR2, isEmp IN BOOLEAN )
IS
CURSOR get_emp IS
SELECT name
FROM EMP;
CURSOR get_std IS
SELECT name
FROM STUDENT;
BEGIN
IF isEmp THEN
OPEN get_emp ;
FETCH get_emp INTO name_ ;
CLOSE get_emp ;
ELSE
OPEN get_std ;
FETCH get_std INTO name_ ;
CLOSE get_std ;
END IF;
RETURN name_;
END Get_Details_On_Condition;
Using if .. else construct is not proper (neither supported). You can use REF cursor to achieve the same like below.
DECLARE type cur1 REF CURSOR;
c1 cur1;
BEGIN
IF (variable1 := 'true') THEN
OPEN c1 FOR 'SELECT * FROM STUDENT';
ELSE
OPEN c1 FOR 'SELECT * FORM EMP';
END IF ;
END;
Idea taken from Oracle Community Forum Post
NOTE: I didn't included the entire code block (I mean cursor processing, closing etc) cause the main concern here is "How he will declare/define conditional cursor". So, pointed that particular in my code snippet. Since, rest of the part like processing the cursor and closing can be directly be found in Oracle specification.
For a complete code block, you can refer the answer given by Harsh
I would prefer to solve this without using dynamic SQL. If the code to process the results is the same for both tables, then it is reasonable to assume that the columns are the same (or equivalent) as well. My inclination would be to solve this using UNION and sub-queries:
DECLARE
CURSOR cur1 IS
SELECT a, b, c, d
FROM emp
WHERE NOT EXISTS
(SELECT *
FROM table1
WHERE s.key = 'xxxxx' AND t.VALUE = 'true')
UNION ALL
SELECT a, b, c, d
FROM student
WHERE EXISTS
(SELECT *
FROM table1
WHERE s.key = 'xxxxx' AND t.VALUE = 'true');
BEGIN
--Stored procedure logic
END;
The link provided by Rahul indicates the correct way to solve the problem. From the Oracle community forum post posted by Rahul, I have taken the code snippet through which the code could run successfully.
Rahul: Please do not take this as a redundant answer as I could not comment on your answer to help shyam to take the code snippet in the link posted by you.
Declare
TYPE cv_typ IS REF CURSOR;
cv cv_typ;
Begin
If(condition1 is TRUE) then
open cv FOR
'Select * from table_name1';
EXIT WHEN cv%NOTFOUND;
ELSE
open cv FOR
'Select * from table_name2';
EXIT WHEN cv%NOTFOUND;
End If;
CLOSE cv;
END;
Thanks & Regards,
Harsh
You can move the OPEN outside IF statement:
DECLARE type cur1 REF CURSOR;
c1 cur1;
vSQL VARCHAR2(1000);
BEGIN
IF (variable1 = 'true') THEN
vSQL := 'SELECT * FROM STUDENT';
ELSE
vSQL := 'SELECT * FORM EMP';
END IF;
OPEN c1 FOR vSQL;
--procedure logic
CLOSE c1;
END;

How to repeat Select statements in a loop in Oracle?

I am reading a lot of stuff about repeating Select statements within a loop but I am having some difficulties as I have not found something clear till now. I want to execute some queries (Select queries) several times, like in a FOR loop. Can anyone help with some example please?
The basic structure of what you are asking can be seen below.
Please provide more information for a more specific code sample.
DECLARE
l_output NUMBER;
BEGIN
FOR i IN 1..10 LOOP
SELECT 1
INTO l_output
FROM dual;
DBMS_OUTPUT.PUT_LINE('Result: ' || l_output);
END LOOP;
END;
PS: If you need to enable output in SQL*Plus, you may need to run the command
SET SERVEROUTPUT ON
UPDATE
To insert your results in another table:
DECLARE
-- Store the SELECT query in a cursor
CURSOR l_cur IS SELECT SYSDATE DT FROM DUAL;
--Create a variable that will hold each result from the cursor
l_cur_rec l_cur%ROWTYPE;
BEGIN
-- Open the Cursor so that we may retrieve results
OPEN l_cur;
LOOP
-- Get a result from the SELECT query and store it in the variable
FETCH l_cur INTO l_cur_rec;
-- EXIT the loop if there are no more results
EXIT WHEN l_cur%NOTFOUND;
-- INSERT INTO another table that has the same structure as your results
INSERT INTO a_table VALUES l_cur_rec;
END LOOP;
-- Close the cursor to release the memory
CLOSE l_cur;
END;
To create a View of your results, see the example below:
CREATE VIEW scott.my_view AS
SELECT * FROM scott.emp;
To view your results using the view:
SELECT * FROM scott.my_view;

Oracle: IN function within an IF block

I have a cursor c2, with one column 'note', containing numbers.
I want to check, whether the column contains 5 and change my local variable if so.
this:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
IF c2.note IN(5) THEN
result := 'contains 5';
DBMS_OUTPUT.PUT_LINE(result);
END;
/
doesnt work.
please help!
In your code, you're declaring a cursor but you are never opening it and never fetching data from it. You'd need, presumably, some sort of loop to iterate through the rows that the cursor returned. You'll either need to explicitly or implicitly declare a record into which each particular row is fetched. One option to do that is something like
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
FOR rec IN c2
LOOP
IF rec.note IN(5) THEN
result := 'contains 5';
DBMS_OUTPUT.PUT_LINE(result);
END IF;
END LOOP;
END;
/
Note that you also have to have an END IF that corresponds to your IF statement. Naming a cursor c2 is also generally a bad idea-- your variables really ought to be named meaningfully.
You have too loop over the records/rows returned in the cursor; you can't refer to the cursor itself like that:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
FOR r2 IN c2 LOOP
IF r2.note = 5 THEN -- IN would also work but doesn't add anything
result := 'contains 5';
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(result);
END;
/
But if you're just testing for the existence of any record with value 5 then you don't need a cursor:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
BEGIN
SELECT max('contains 5')
INTO result
FROM student
WHERE note = 5;
DBMS_OUTPUT.PUT_LINE(result);
END;
/
If there are any rows with five you'll get the 'contains 5' string; if not you'll get null. The max() stops an exception being thrown if there are either zero or more than one matching records in the table.
You are missing an END IF and need to LOOP over the cursor. The procedure name is included in the final END.
But using IN should work. Like this:
CREATE OR REPLACE PROCEDURE proc
IS
result varchar(50);
cursor c2 is
SELECT note
FROM student;
BEGIN
FOR rec in c2
LOOP
IF rec.note IN (5) THEN
result := 'contains 5';
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE(result);
END proc;
/