PL/SQL: Error when creating sequence - oracle

I'm quite new to PL/SQL, and am using Oracle SQL Developer to write a procedure which uses a sequence to generate a primary key for some existing data, to write into another DB.
The code in question is under NDA.. Essentially I have the following:
create or replace
PROCEDURE Generate_Data
(
output IN VARCHAR2
)
AS
-- Variables here --
CURSOR myCursor IS
SELECT data1, data2
FROM table;
CREATE SEQUENCE mySequence <-- error on this line
START WITH 0
INCREMENT BY 1;
BEGIN
LOOP
-- snip --
It raises the error PLS-00103, saying it encountered the symbol CREATE when expecting on of the following: begin, function, package, pragma, procedure, ...
I've been following the example at:
http://www.techonthenet.com/oracle/sequences.php

The reason you're getting this error is that you're trying to perform DDL, in this case creating a sequence, within PL/SQL. It is possible to do this, but you must use execute immediate.
As Alex says, you also wouldn't be able to do this in the declare section. It would look something like this:
begin
execute immediate 'CREATE SEQUENCE mySequence
START WITH 0
INCREMENT BY 1';
end;
However, as Padmarag also says, it's highly unlikely that you want to do this within PL/SQL. It would be more normal to create a sequence outside and then reference this later. More generally speaking, performing DDL inside a PL/SQL block is a bad idea; there should be no need for you to do it.
You don't mention what version of Oracle you're using. From 11g the ways in which you could access sequences got extended. If you're using 11g then you can access the sequence by creating a variable and assigning the next value in the sequence, .nextval, to this variable:
declare
l_seq number;
begin
loop
-- For each loop l_seq will be incremented.
l_seq := mysequence.nextval;
-- snip
end;
If you're before 11g you must (outside of DML) use a select statement in order to get the next value:
declare
l_seq number;
begin
loop
-- For each loop l_seq will be incremented.
select mysequence.nextval into l_seq from dual;
-- snip
end;
Please bear in mind that a sequence is meant to be a persistent object in the database. There is no need to drop and re-create it each time you want to use it. If you were to run your script, then re-run it the sequence would happily keep increasing the returned value.
Further Reading
About sequences
Using sequences

You can't create sequence in the DECLARE block of procedure. Move it after BEGIN. It's arguable if it makes sense, though. You probably need to create it outside your procedure in the first place.
Update
Actually, if you truly want it inside BEGIN/END use following:
EXECUTE IMMEDIATE 'CREATE SEQUENCE mySequence START WITH 0 INCREMENT BY 1';

You'd need to create the sequence before using it.
And in the PL/SQL code use
-- Variables here --1
v_seq_val number;
BEGIN
Select mySequence.nextval from dual into v_seq_val
In general SQL is for DDL(Data Definition Language) and PL/SQL is for DML(Data Manipulation Language) and logic.
If you wanted you could do Create from PL/SQL, but I think that's not what you want over here.

Related

How to use DB link in a variable in for loop

I have below query which I am trying to write in a procedure after begin clause. I dont want to use it as a cursor because of some dependency.
I want to make my db link dynamic instead of hardcoding it and for this reason i put my entire for loop in variable. If i take the variable out then my procedure is working fine. I dont want to change logic of my code while trying to make dblink dynamic.
But this part of loop is not working and throwing an error as
encounter the symbol end of the file when expecting one of the following:
PROCEDURE TMP_CHECK
IS
open CS for NESS_QUERY;
loop
fetch CS into REC;
exit when CS%notfound;
INSERT INTO TMP_Data(ID,NAME,ID_TST,CHK_DATE,VALUE,CHECK,SOURCE) VALUES
(IN_SEQ_NO,DB_NAME,DB_ID,REC.DAY_ID,REC.nb_ord,'ORDS','LEOSOFT');
COMMIT;
END LOOP;
CLOSE CS;
END LOOP;
END;
Dynamic SQL is hard because it turns compilation errors into runtime errors. It looks like your query has several compilation errors: duplicate table aliases, out-of-scope alias references, cross joins between the remote tables (unless that is deliberate, in which case yuck!). So the first thing to do is get the query running as straight SQL, only then make it dynamic.
Also don't include commented code in your template SQL. Things are already hard enough, why make them even harder by doing stuff like this?
ORDER BY
-- TE.market asc,
-- TE.entity asc,
TE.dayiid ASC)'
So, now we've got that out of the way let's look at the logic of what you're trying to do. We cannot drop dynamic segments of PL/SQL into a program. This just won't work ...
LQUERY='
FOR REC IN(
SELECT
... because you have not written a complete PL/SQL statement. But there is a way to do what you want: use a cursor variable. We can open a ref cursor for static and dynamic queries. Find out more.
The following is for illustrative purposes only: you haven't explained your business logic, so this is not necessarily the best way of doing things. But it should solve your immediate problem:
declare
....
l_order number;
l_dayiid number;
l_ety_id number;
rc sys_refcursor;
begin
...
FOR IIS_DB IN C_DB
LOOP
IN_DB_LINK:=LEO_DB.DATABASE_LINK;
IN_DAY:=LEO_DB.DAY_ID;
open rc for
'SELECT order,dayiid,ety_id
from ...
ORDER BY TE.dayiid ASC)';
loop
fetch rc into l_order, l_dayiid, l_ety_id;
exit when rc%notfound;
...
end loop;
close rc;
" PLS-00487: Invalid reference to variable 'REC'"
I think your problem is this:
fetch CS into REC;
You have defined REC as a string but clearly it should be a record type, which needs to match the projection of the query you're fetching. So you need to define something like this:
Type rec_t is record (
nb_ord number,
day_id number,
entity number
);
REC rec_t;
Now you can fetch a record into REC and reference its attributes.
Incidentally the nvl() you've written to supply NB_ORD is wrong. The first argument is the one you are testing for null: 500 will never be null so that's what you'll get for every row. You need to swap the parameters round.

dbms_output.put_line doesn't work inside a Cursor For loop of a stored procedure

I am having a bizarre problem that seems very specific to CURSOR FOR Loops inside of a stored procedure. For clarity, I am using Oracle within DBeaver and am attempting to loop over all of the columns in a table and print out the results of a select statement.
I don't have access to the exact code but this is functionally approximate:
CREATE OR REPLACE PROCEDURE column_null(table_name_in IN VARCHAR2)
AS
str_query VARCHAR2(1000);
temp_number NUMBER(10);
CURSOR col_cursor IS
SELECT * FROM user_tab_cols
WHERE table_name = table_name_in;
BEGIN
FOR c_id IN col_cursor
LOOP
str_query := 'select COUNT(*) FROM ' || table_name_in ||
' WHERE ' || c_id.column_name || ' IS NOT NULL';
EXECUTE IMMEDIATE str_query INTO temp_number;
DBMS_OUTPUT.PUT_LINE(temp_number);
END LOOP;
END;
Now, the bizarre part is that if I do this exact same code block outside of a stored function (minus an extra DECLARE keyword), it works as expected. Even if I try to just echo out 'Hello' within a loop it works as expected, but as soon as it becomes a stored procedure it stops working. I've been testing this for hours today, and am completely baffled; for reference, I have only recently become acquainted with PL/SQL so its mysteries escape me.
Furthermore, it seems specific to CURSOR FOR loops; if I replace the Cursor For loop with a generic numeric loop (i.e. FOR c_id IN 1 .. 10), a procedure will produce output just fine. And it isn't just DBMS_OUTPUT.PUT_LINE that's affected; pretty much everything that goes on inside the Cursor For loop is ignored in a stored procedure, including variable updates, even though they work fine otherwise in normal PL/SQL blocks.
To summarize: Works fine as a PL/SQL block, works fine in a numeric for loop, but for some reason the exact combination of stored procedure and cursor for loop causes no output to be produced; in fact from my testing it seems like nothing meaningful happens within the cursor for loop of a stored function.
Is this a DBeaver bug? A PL/SQL oddity? I'm posting here because I'm ignorant as to whether this is expected behavior due to how Procedures and/or Cursor For loops work, or if this is a bug of some kind.
What you have done is declaring a procedure. Now that you have declared it, you have to call it using a program like bellow. Most likely it will generate outputs.
Option 01
set serveroutput on;
Declare
v_table_name_in IN VARCHAR2(499);
Begin
v_table_name_in := 'your table name';
column_null(table_name_in => v_table_name_in);
end;
Option 02
Get a return parameter. ideally a table type as out parameter. and inside the above code, loop through it and print the value.
Option 03.
Log the outputs into a log table.
I found the error was solved by simply adding AUTHID current_user to the procedure; apparently the procedure didn't have permission to access the table it was trying to select. Strangely though, no error was produced when trying to run the procedure; it just didn't produce any output.

Create and execute an Oracle Stored Procedure for a select query in SQL Developer

I am using Oracle SQL Developer with Oracle 11g.
I face a strange issue creating a simple stored procedure for a Select query that doesn't need any input parameters as such. It just selects from a user defined function from the "dual" table.
These are the issues I face:
I am not able to create a procedure with no input parameters (because I don't need to use any parameter value in the select!). But the syntax does not allow me to have zero parameters, it demands a REF_CURSOR out parameter. Is the REF_CURSOR a compulsory thing in SQL Developer procedures? Is it anything to do with procedures involving a Select query?
The select query demands an INTO clause (a variable to copy the query result) in SQL developer. Is it mandatory?
Even if I used an INTO clause, I can't figure out the syntax to declare a temporary variable to copy the query result into this variable. So that I can use this out variable in my program snippet.
This is my procedure block:
Create or Replace PROCEDURE Getmarketdetails
AS
DECLARE temp varchar;
BEGIN
SELECT *
INTO temp from dual;
END Getmarketdetails;
I get these errors on compiling the procedure:
PLS-00103: Encountered the symbol "DECLARE" when expecting one of
the following: begin function pragma procedure subtype type
current cursor delete exists prior external language The
symbol "begin" was substituted for "DECLARE" to continue.
PLS-00103: Encountered the symbol "end-of-file" when expecting one of the following: ( begin case declare end exception exit for goto if loop mod null pragma raise return select update while with << continue close current delete fetch lock insert open rollback savepoint set sql execute commit forall merge pipe purge .
All I need is the perfect script syntax to create the stored procedure for this and also execute it using the exec command. And some clarifications to questions raised above. Appreciate if someone can oblige ! :)
Your syntax is incorrect - you need to declare a length for your varchar and you don't need the declare.
Create or Replace PROCEDURE Getmarketdetails
AS
temp varchar(100);
BEGIN
SELECT *
INTO temp from dual;
END Getmarketdetails;
Create or Replace PROCEDURE Getmarketdetails
AS
temp varchar2(20);
BEGIN
SELECT 'stack overflow' INTO temp from dual;
Dbms_output.put_line(temp);
END Getmarketdetails;
Some modification done in your procedure. Don't write declare and mention variables as per your need.

Can Oracle Program Return Results from global Temporary Table

Here's a piece of Oracle code I'm trying to adapt. I've abbreviated all the details:
declare
begin
loop
--do stuff to populate a global temporary table. I'll call it 'TempTable'
end loop;
end;
/
Select * from TempTable
Right now, this query runs fine provided I run it in two steps. First I run the program at the top, then I run the select * to get the results.
Is it possible to combine the two pieces so that I can populate the global temp table and retrieve the results all in one step?
Thanks in advance!
Well, for me it depends on how I would see the steps. You are doing a PL/SQL and SQL command. I would rather type in those into a file, and run them in one command (if that could called as a single step for you)...
Something like
file.sql
begin
loop
--do stuff to populate a global temporary table. I'll call it 'TempTable'
end loop;
end;
/
Select *
from TempTable
/
And run it as:
prompt> sqlplus /#db #file.sql
If you give us more details like how you populate the GTT, perhaps we might find a way to do it in a single step.
Yes, but it's not trivial.
create global temporary table my_gtt
( ... )
on commit preserve rows;
create or replace type my_gtt_rowtype as object
( [columns definition] )
/
create or replace type my_gtt_tabtype as table of my_gtt_rowtype
/
create or replace function pipe_rows_from_gtt
return my_gtt_tabtype
pipelined
is
pragma autonomous_transaction;
type rc_type is refcursor;
my_rc rc_type;
my_output_rec my_gtt_rectype := my_gtt_rectype ([nulls for each attribute]);
begin
delete from my_gtt;
insert into my_gtt ...
commit;
open my_rc for select * from my_gtt;
loop
fetch my_rc into my_output_rec.attribute1, my_output_rec.attribute1, etc;
exit when my_rc%notfound;
pipe_row (my_output_rec);
end loop;
close my_rc;
return;
end;
/
I don't know it the autonomous transaction pragma is required - but I suspect it is, otherwise it'll throw errors about functions performing DML.
We use code like this to have reporting engines which can't perform procedural logic build the global temporary tables they use (and reuse) in various subreports.
In oracle, an extra table to store intermediate results is very seldom needed. It might help to make things easier to understand. When you are able to write SQL to fill the intermediate table, you can certainly query the rows in a single step without having to waste time by filling a GTT. If you are using pl/sql to populate the GTT, see if this can be corrected to be pure SQL. That will almost certainly give you a performance benefit.

how do oracle stored procedures (w/ cursors) work?

I have a following oracle stored procedure
CREATE OR REPLACE
PROCEDURE getRejectedReasons
(
p_cursor IN OUT SYS_REFCURSOR)
AS
BEGIN
OPEN p_cursor FOR SELECT * FROM reasons_for_rejection;
END;
However, when I run this stored procedure in sql-developer then I dont see anything. I just see something like this:
Connecting to the database oracleLocal.
Process exited.
Disconnecting from the database oracleLocal.
I'm coming from MS sql server and am used to seeing actual results when running a stored procedure like this. Is this stored procedure not returning results because I am using a cursor??
The stored procedure is returning something it's just you aren't doing anything with the results.
You can do this simply by running the following script in SQLDeveloper:
VARIABLE csr REFCURSOR;
EXEC getRejectedReasons(:csr); -- the colon identifies the parameter as a variable
PRINT csr;
Another method is to fetch each row and do some sort of processing:
DECLARE
-- sys_refcursor is weakly typed
refcsr SYS_REFCURSOR;
-- define a record so we can reference the fields
rej_rec Reasons_for_Rejection%ROWTYPE;
BEGIN
getRejectedReasons(refcsr);
-- loop through the results
LOOP
-- gets one row at a time
FETCH refcsr INTO rej_rec;
-- if the fetch doesn't find any more rows exit the loop
EXIT WHEN refcsr%NOTFOUND;
-- Do something here.
-- For example : DBMS_OUTPUT.PUT_LINE(rej_rec.reason_desc);
END LOOP;
END;
You opened the cursor. You didn't select anything from it, update it, or advance it.
All open does, effectively, to select the matching rows into temporary memory, so you can advance the cursor row by row. Which you didn't do.
One of the differences between Oracle and SQL Server is that the latter returns result sets naturally. I'd use a function, by the way.
In Oracle, functions typically return a single element. Cursors came later.
There's some documentation online that will help you understand the use of refcursor bind variables. Here's one such for SQL*Plus:
http://download.oracle.com/docs/cd/B19306_01/server.102/b14357/ch5.htm#sthref1122
I think in SQL Developer you can do the same thing with autoprint on, although I haven't tested that.
Found a blog that also discusses something similar:
http://vadimtropashko.wordpress.com/cursors/
ETA: Ok. Ignore what I wrote. Listen to someone else. Apparently it's wrong, as I got down voted.
What tpdi said is correct. You have to do something with the cursor after you declare it.
Here's an example using two cursors in nested loops
PROCEDURE update_insert_tree (exid_in IN NUMBER, outvar_out OUT VARCHAR2)
IS
nxtid NUMBER;
phaseid NUMBER;
rowcounter1 NUMBER;
BEGIN
rowcounter1 := 0;
outvar_out := 0;
FOR acur IN (SELECT dept_exercise_id, phase
FROM ep_dept_exercise
WHERE exercise_id = exid_in)
LOOP
<<dept_loop>>
FOR thecur IN (SELECT document_name, thelevel, sortnum, type_flag,
ex_save_id
FROM ep_exercise_save
WHERE exercise_id = exid_in)
LOOP
phaseid := acur.phase;
IF phaseid = 0
THEN
phaseid := 10;
UPDATE ep_dept_exercise
SET phase = 10
WHERE dept_exercise_id = acur.dept_exercise_id;
END IF;
<<doc_loop>>

Resources