I have a pretty basic procedure that I am trying to create. I just need to create a record for a single row from a table. What am I missing? The error code that I am receiving is "encountered the symbol declare when expecting...."
Create or Replace Procedure Luke as
Declare
Type type_basket is record(Term_code section.term_code%type,
Subject_Code section.subject_code%type,
Course_Number section.course_number%type,
Section section.section%type);
Rec_Basket type_basket;
Begin
Select term_code, subject_code, course_number, section into Rec_basket
from Enrollment
where term_code=201201 and course_number=105;
dbms.output_put.line(rec_basket.term_code);
end;
DECLARE is only needed for anonymous blocks and subblocks, you don't use it to declare stored procedure variables:
Note:
The declarative part of a subprogram does not begin with the keyword DECLARE, as the declarative part of an anonymous block does.
So remove that line:
Create or Replace Procedure Luke as
Type type_basket is ...
Rec_Basket type_basket;
Begin
Select ...
end;
/
You could use a %rowtype variable instead of explicitly declaring a record type, and select the whole row into that, but I assume your assignment is specifically about records.
Related
I am going to write a procedure that should get an input parameter which its type is Java.Util.Collection. Indeed, I have a report in Jaspersoft Studio that is received a Java.Util.Collection parameter and generated report based on the parameter's data. Also, I want to write my queries in the database package and I do not know how to pass an input parameter of type Java.Util.Collection to the procedure of PL/SQL.
Besides, I have declared a type in the package like this
TYPE COLLECTION_TYPE IS TABLE OF VARCHAR2(10);
and use it as a parameter type in procedure, while an error is raised when I want to compile my package that said
Error: PLS-00371: at most one declaration for 'COLLECTION_TYPE' is permitted
Could you please guide me to solve this problem?
My PL/SQL code is like below code.
Thanks in advance.
PROCEDURE procedure_name(I_ORGANIZATION IN COLLECTION_TYPE,
O_CURSOR OUT SYS_REFCURSOR) AS
BEGIN
OPEN O_CURSOR FOR
SELECT TBL1.NAME
FROM tbl_name TBL1
WHERE TBL1.ORGANIZATIONID IN (I_ORGANIZATION);
END;
In your code wrong is.
`WHERE TBL1.ORGANIZATIONID IN (I_ORGANIZATION);`
there is not such operation with collection type.
replace it with member_of TBL1.ORGANIZATIONID memeber_of I_ORGNAIZATION or TBL1.ORGANIZATIONID in (select column_value from table(I_ORGANIZATION)).
The error Error: PLS-00371. is generated in other place.
You are using the same identifier name twice. Example.
declare
a varchar2(100);
a number;
begin
a := 'aa';
end;
The below simple procedure is suppose to provide grants to the user scott in oracle.
The value for &scott_SCHEMA is already defined in a seperate file(define_variable.sql) and the value is getting substituted correctly, but im getting the error(as specified in the bottom of the script), a help is much appreciated.
SET SERVEROUTPUT ON
declare
l_sql varchar2(3200);
begin
for i in ( select table_name as oname,'TABLE' as type from all_tables where owner='HR' AND table_name not like 'BIN$%' union all select view_name as oname,'VIEW' as type from all_views where owner='HR' and view_name not like 'BIN$%' )
loop
if i.type = 'TABLE' then
dbms_output.put_line(l_sql);
l_sql:= 'grant select,insert,update,delete on hr.'||i.oname||' to :owner with grant option';
execute immediate l_sql using '&scott_SCHEMA';
else
l_sql:= 'grant select on hr.'||i.oname||' to :owner with grant option';
end if;
end loop;
end;
/
*declare
*
ERROR at line 1:
ORA-00987: missing or invalid username(s)
ORA-06512: at line 12*
You can't use bind variables in place of identifiers (specifically schema or object names) in DDL or DML statements, they can only be used in place of value expressions.
Since you're using substitution variables, you could just place it in the sql statement itself:
l_sql:= 'grant select,insert,update,delete on hr.'||i.oname||' to &scott_SCHEMA with grant option';
and execute it with out passing it as a parameter:
execute immediate l_sql;
On a side note, your DBMS_OUTPUT line should come after you assignment to l_sql, otherwise you'll miss outputting one or more of the statements being processed. A good place would be between the assignment statement and the execute statement.
I have a scenario where I have to perform dynamic assignment to PLSQL type variable.
For example, below is a simple assignment of value to type my_tab_type. fn_get_value(varchar2) function returns value based on string passed.
my_tab_type.table_name.LEAD := fn_get_value('LEAD_ID');
Like these there will be hundreds of assignments. I want to store the mapping (LEAD,LEAD_ID) in a table, fetch these mapped values in a cursor and dynamically create assignment statement.
I want to avoid execute immediate or dbms_sql.execute for each dynamic assignment created as performance will be affected. Please help me come up with an feasible and effective approach.
(You might have a XY-problem.)
You don't tell us the types in your question so I assume nested table of records.
You can't assign a PL/SQL record "dynamically" without using dynamic PL/SQL (i.e. running PL/SQL with execute immediate). This also means that all uses of such "dynamically" created record have to take place dynamically. That's not the way PL/SQL is supposed to be used.
However if you can change your data type to associative array you can archive your goal with a plain boring static well-mannered PL/SQL !
Below you'll find an example that should get you started.
Example
create table so60 (
from_ varchar2(20)
,to_ varchar2(20)
);
insert into so60 values('A_FROM', 'A_TO');
insert into so60 values('B_FROM', 'B_TO');
insert into so60 values('C_FROM', 'C_TO');
declare
-- foo_t is an associative array
type foo_t is table of varchar2(32767) index by varchar2(32767);
v_foo foo_t;
function f(p_x in varchar2) return varchar2 is
begin
return lower(p_x);
end;
begin
-- initialize from table
for i in (select * from so60)
loop
v_foo(i.to_) := f(i.from_);
end loop;
dbms_output.put_line(v_foo('B_TO'));
end;
/
Example run
SQL> #so60
b_from
PL/SQL procedure successfully completed.
SQL>
I've created an anonymous PLSQL block to test and I'm running into an issue with the formatting.
set serveroutput ON
BEGIN
FOR I IN (SELECT DISTINCT do.SUBOBJECT_NAME from dba_objects do WHERE do.object_name='MY_TABLE' AND do.OBJECT_TYPE='TABLE PARTITION') LOOP
dbms_output.put_line(I.subobject_name);
SELECT
t.field
INTO
some_var
FROM
MY_TABLE PARTITION(I.subobject_name) t;
END LOOP;
END;
However I get several compilation errors, which I believe are related to the fact that I.subobject_name is a string. I believe the PARTITION function wants an actual partition symbol(proper term for this?), but I can't give it in this loop.
Is there any kind of casting function that can perform what I'm looking for?
Partition IS NOT A FUNCTION. Partition is keyword
In your context your whole statement is static , thus you CANNOT pass partition name into it; partition name must be specified at compile time.
You can re-create your statement dynamically and then pass partition name in the loop -
a-la you are doing it. Just make sure you will concatenate string and not use bind variables, or your statement at run time won't be parsed and won't run.
Name for the symbol is table partition
I need a little stored procedure to do the following logic?
procedure_name(seq_name IN varchar2(50), block_count IN int, return_ids OUT)
loop from 1 to block_count
return_ids := select 'seq_name'||.nextVal from dual;
end loop
return return_ids
Basically what I want to do is have a stored procedure that lets me pass in a sequence name, how many IDs I need and return to me the generated listed of IDs that I can use in JAVA. The reason to do this for me is to return a list of IDs that I can use in JAVA and no one else is using those sequence IDs. Where they will be used in some other bulk inserts later down the line. In essence, reserve a block of sequence IDs.
Here is one way to return an array from PL/SQL procedure.
Create a collection type of numbers, initialize it in your procedure and populate it with numbers to return. For example:
create or replace type narray as table of number;
create or replace procedure get_seq_ids(seq_name in varchar2,
block_count in number, return_ids out narray)
as
begin
return_ids := narray();
return_ids.extend(block_count);
for i in 1 .. block_count
loop
execute immediate 'select ' || seq_name || '.nextval from dual'
into return_ids(i);
end loop;
end;
/
I'd be concerned over the logic that requires IDs to be generated before records are inserted into the database.
Alternatively you may want to consider inserting rows first, selecting the ids from the rows, and then using an update statement to do your bulk operation. This however is still not as preferable as having the Java code not depend on ids until after the actual information is ready to be inserted.
You can push your information into XML (or any other data format your database can understand) and then call a stored procedure to do the bulk inserts.
Another option may be to use the RETURNING clause to return the sequence values automatically after the insert.
IMHO, the best thing you can do is just referencing sequence_name.nextval right in your INSERT INTO, in the VALUES clause.
You said you want to avoid others using the same IDs. Referencing this site:
The sequence (or Oracle, for that matter) ensures that no other session or other call to nextval within the same session gets the same number from the sequence.
So, the uniqueness of a sequence' numbers are guaranteed in Oracle.
Here's what I do for the Java application I support (which also uses bulk inserts into deeply hierarchical tables)
PROCEDURE get_nextvals
(
p_values OUT SYS_REFCURSOR,
p_count IN PLS_INTEGER
)
IS
-- return the next p_count values from the PK sequence
BEGIN
OPEN p_values FOR
SELECT
<schema>.<sequence>.nextval
FROM
dual
CONNECT BY
LEVEL <= p_count
;
END;
It was easier to just pass a cursor out to java than having the app use a table type defined in the DB.