Execute immediate and missing invalid option - oracle

The procedure is for cloning a table. It receives the names of the two tables in parameters, and when called it should clone the table.
CREATE OR REPLACE PROCEDURE CLONE_TABLE(
table_source VARCHAR2,
table_destination VARCHAR2)
is
begin
execute immediate 'create table ' || table_destination || 'as select* from '
|| table_source;
end;
But when i call the procedure, I receive error ORA-00922: Missing or invalid option.
BEGIN
CLONE_TABLE('example','example_new')
END
I don't realise what's the problem and how should I fix it.

Writing dynamic SQL is hard, because what should be compilation errors become runtime errors. It is therefore crucial to develop a cool eye when looking at your own code. You have to be the compiler.
It helps to assemble the statement as a variable first. That way you can display the statement in the case of error, which makes debugging easier.
CREATE OR REPLACE PROCEDURE CLONE_TABLE(
table_source VARCHAR2,
table_destination VARCHAR2)
is
stmt varchar2(32767);
begin
stmt := 'create table ' || table_destination || 'as select* from '
|| table_source;
execute immediate stmt;
exception
when others then
dbms_output.put_line(stmt);
raise;
end;
If you had done that if would have been obvious that you were missing a space in front of the as. So your executed statement was this:
create table example_newas select* from example
The space between select and * is optional, but code looks better with one.

There are only 2 problem and couple of suggestions for your code.
Missing space before as select...
Calling of procedure, use exec or call statements.
Suggestions:
Make sure to do proper error handling (so if a table doesn't exists, or if a table destination already exists, you should get proper return message.
Also separate each keyword with space as other answers suggested, like between select and *. But not doing it will not give you error.
CREATE OR REPLACE PROCEDURE CLONE_TABLE(
table_source VARCHAR2,
table_destination VARCHAR2)
is
begin
execute immediate 'create table ' || table_destination || ' as select* from '
|| table_source;
end;
EXEC CLONE_TABLE('example','example_new')

Use this:
CREATE OR REPLACE PROCEDURE CLONE_TABLE (table_source VARCHAR2,
table_destination VARCHAR2)
IS
v_sql varchar2(1000);
BEGIN
v_sql:= 'create table '
|| table_destination
|| ' as select * from '
|| table_source;
dbms_output.put_line(v_sql);
EXECUTE IMMEDIATE v_sql;
END;
The best way to handle issue like you got is to display first what you are trying to execute immediate. You get to know your fault.

There is should be one space before as select.. and in between select and * near select*....
SQL> CREATE OR REPLACE PROCEDURE CLONE_TABLE(
table_source VARCHAR2,
table_destination VARCHAR2)
is
begin
execute immediate 'create table ' || table_destination || ' as select * from ' || table_source;
end;
/ 2 3 4 5 6 7 8
Procedure created.
SQL> create table example(id number);
Table created.
SQL> exec CLONE_TABLE('example','example_new');
PL/SQL procedure successfully completed.

Related

How to create new table and insert using function in plsql?

I want to create a function in plsql to create table dynamically
Create or replace procedure p_dynamic_table (p_table varchar2,
col_specs varchar2)
as
sql_stmt varchar2 (2000);
begin
sql_stmt := 'CREATE TABLE' || p_table || '(' || col_specs || ')';
EXECUTE IMMEDIATE sql_stmt;
end;
Put a space after TABLE and before the ending single quote.
Then your procedure will "work". It will not be a function, but a procedure. Also, it is a procedure that has no added value so I don't understand why you want to code it.
Best regards,
Stew Ashton
Are you want to create Procedure or Function?
You mentioned Function in Topics header but try to create Procedure (according the mentioned code by you).
If you don't want to create constraints & Indexes then your Procedure is okay without
SQL_STMT := 'CREATE TABLE' || P_TABLE || '(' || COL_SPECS || ')';
you must be put SPACE after 'CREATE TABLE' which is already mentioned in answer of Stew Ashton.
According to what I understood, I tried to mention the solution below.
PROCEDURE
CREATE OR REPLACE PROCEDURE P_DYNAMIC_TABLE (P_TABLE VARCHAR2,
COL_SPECS VARCHAR2)
AS
SQL_STMT VARCHAR2 (2000);
BEGIN
SQL_STMT := 'CREATE TABLE ' || P_TABLE || '(' || COL_SPECS || ')';
EXECUTE IMMEDIATE SQL_STMT;
END P_DYNAMIC_TABLE;
Execute Procedure
EXEC P_DYNAMIC_TABLE('TEST_TBL','COLUMN_1 NUMBER , COLUMN_2 VARCHAR2(100), COLUMN_3 DATE');
You can try this!
create or replace procedure f2
is
begin
execute immediate 'create table &nds_ddl(&c1 &datatype)';
end;
Then execute the procedure f2
i.e
exec f2;
Select * from table_name_you_entered;

Use Input Parameter of procedure to declare %Rowtype record

I am trying to insert the data into a table using Bulk Collect.
Here is the code:
create or replace procedure insert_via_bulk_collect authid current_user
as
lc_status number;
cursor lc_old_tb_data is select * from old_table_name;
type lc_old_tb_type is table of old_table_name%rowtype;
lc_old_tb_row lc_old_tb_type;
begin
open lc_old_tb_data;
loop
fetch lc_old_tb_data bulk collect into lc_old_tb_row;
forall i in 1..lc_old_tb_row.count
insert into new_table_name values lc_old_tb_row(i);
commit;
exit when lc_old_tb_data%notfound;
end loop;
close lc_old_tb_data;
end insert_via_bulk_collect;
It's working for me. But I want to pass the table name dynamically.
like
insert_via_bulk_collect(new_tb_nm varchar2(30), old_tb_name varchar2(30))
But I am not able to use those variables in cursor and declaring the record of OLD_TABLE%rowtype.
Is there a way to do this?
Try below code
CREATE OR REPLACE PROCEDURE INSERT_VIA_BULK_COLLECT
(NEW_TABLE_NAME varchar2, OLD_TABLE_NAME varchar2)
as
l_str varchar2(4000);
begin
l_str := 'declare
CURSOR lc_old_tb_data IS SELECT * FROM ' || OLD_TABLE_NAME || ';
TYPE lc_old_tb_type IS TABLE OF '|| OLD_TABLE_NAME || '%rowtype;
lc_old_tb_row lc_old_tb_type;
begin
Open lc_old_tb_data;
loop
fetch lc_old_tb_data bulk collect into lc_old_tb_row;
FORALL i IN 1..lc_old_tb_row.count
INSERT INTO ' || NEW_TABLE_NAME || ' VALUES lc_old_tb_row(i);
COMMIT;
exit when lc_old_tb_data%notfound;
end loop;
close lc_old_tb_data;
end; ';
dbms_output.put_line (l_str);
EXECUTE IMMEDIATE l_str;
end insert_via_bulk_collect;
You can add Exception handing. Also it is recommended to add limit for fetching of data as below fetch lc_old_tb_data bulk collect into lc_old_tb_row l_size; you can put l_size value as per your requirement.
As mentioned in comments, it is better to use direct insert ... select .... In your case, you can combine this way with execute immediate:
create or replace procedure insert_via_bulk_collect(new_tb_nm varchar2, old_tb_name varchar2) is
begin
execute immediate 'insert into ' || new_tb_nm ||
'(<columns list>) select <columns list> from ' || old_tb_nm;
end;

Execute Immediate Statement issue - Variable field name and table name

Hi there I am trying to build a function to reset a sequence to synch with table ID's which have gotten out of synch with the sequence. Function is as follows:
create or replace
FUNCTION P_FNC_SEQUENCERESET(sourceTable IN VARCHAR2, idField IN VARCHAR2, seqname VARCHAR2) RETURN NUMBER
IS
ln NUMBER;
ib NUMBER;
maxId NUMBER;
newValue NUMBER;
diffValue NUMBER;
interimValue NUMBER;
sqlStmt VARCHAR2(2000);
BEGIN
-- Get the maximum of the id field
EXECUTE IMMEDIATE 'SELECT MAX(' || idField || ') INTO ' || maxId || ' FROM ' || sourceTable;
...code continues...
My understanding of the EXECUTE IMMEDIATE statement leads me to believe that this should be possible, however when executed I get this error:
ORA-00936: missing expression
ORA-06512: at "PSALERT_ADMIN.P_FNC_SEQUENCERESET", line 16
ORA-06512: at line 11
It would need to look something like this:
EXECUTE IMMEDIATE 'SELECT ' || idField ||' FROM ' || sourceTable into maxid;
the keyword "into" is not part of the string that is dynamically executed, but it is part of the syntax of the "execute immediate" statement
https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/executeimmediate_statement.htm

Retrieve next value of Oracle sequence via stored procedure

I have created a SEQUENCE in Oracle11g which is working as expected. The challenge is that I need to retrieve the next value of the sequence via a stored procedure (because the application I'm using can only call a stored procedure not a sql statement).
I have looked all over and the closest I've found is this (here: https://community.oracle.com/thread/2216649):
CREATE OR REPLACE PROCEDURE CSDWH.CSDWH_RETURN_SEQ_NUM
(SEQ_NAME IN VARCHAR2,SEQUENCE_OUT OUT NUMBER)
IS
BEGIN
EXECUTE IMMEDIATE 'SELECT ' || SEQ_NAME || '.NEXTVAL from dual' INTO sequence_out;
END;
The issue with this is the way of execution:
declare
l_val number;
begin
csdwh_return_seq_num( 'FOO_SEQ', l_val );
dbms_output.put_line( l_val );
end;
The application I need to execute the stored procedure only have a connection string alias, a stored procedure name, and a return parameter field to be specified. The way to execute this stored procedure required 6 lines of code.
Is there any way to achieve the above with the EXECUTE command?
i.e. EXECUTE CSDWH.CSDWH_RETURN_SEQ_NUM
that would return the value?
just create a function instead of procedure
CREATE OR REPLACE FUNCTION GET_SEQ(SEQ_NAME IN VAECHAR2) AS
DECLARE
seq_out NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT ' || SEQ_NAME || '.NEXTVAL from dual' INTO seq_out;
RETURN seq_out;
END;
then you can use select get_seq('foo_seq') from dual

Create nested procedure in Oracle 10g

I want to create a nested procedure where in the 1st procedure, I want to create a table dynamically with 2 columns and in the 2nd procedure, I want to insert values into that table.
Below is the code I am trying to use; what am I doing wrong?
CREATE or replace PROCEDURE mytable (tname varchar2)
is
stmt varchar2(1000);
begin
stmt := 'CREATE TABLE '||tname || '(sname varchar2(20) ,sage number (4))';
execute immediate stmt;
end;
create PROCEDURE mytable1 (emp_name varchar2,emp_age number,tname varchar2)
is
stmt1 varchar2(1000);
begin
stmt1 := 'insert into '||tname||' values ('Gaurav' ,27)';
execute immediate stmt1;
end;
There's no need to create a nested procedure here. You can do everything in a single procedure.
Note my use of bind variables in the execute immediate statement
create or replace procedure mytable (
Ptable_name in varchar2
, Pemp_name in varchar2
, Pemp_age in number
) is
begin
execute immediate 'create table ' || Ptable_name
|| ' (sname varchar2(20), sage number (4))';
execute immediate 'insert into ' || Ptable_name
|| ' values (:emp_name, :emp_age)'
using Pemp_name, Pemp_age;
end;
More generally, there's no need to use execute immediate at all; creating tables on the fly is indicative of a poorly designed database. If at all possible do not do this; create the table in advance and have a simple procedure to insert data, should you need it:
create or replace procedure mytable (
, Pemp_name in varchar2
, Pemp_age in number
) is
begin
insert into my_table
values (Pemp_name, Pemp_age);
end;
I would highly recommend reading Oracle's chapter on Guarding Against SQL Injection.
If you really feel like you have to do this as a nested procedure it would look like this; don't forget to call the nested procedure in the main one as the nested procedure isn't visible outside the scope of the first.
create or replace procedure mytable (
Ptable_name in varchar2
, Pemp_name in varchar2
, Pemp_age in number
) is
procedure myvalues (
Pemp_name in varchar2
, Pemp_age in number
) is
begin
execute immediate 'insert into ' || Ptable_name
|| ' values (:emp_name, :emp_age)'
using Pemp_name, Pemp_age;
end;
begin
execute immediate 'create table ' || Ptable_name
|| ' (sname varchar2(20), sage number (4))';
myvalues ( Pemp_name, Pemp_age);
end;
Please see Oracle's documentation on PL/SQL subprograms

Resources