I am writing a stored procedure to update data in a table where I will pass a string with the new data (col1='new values', col2='new 2 values'). But when I am compiling my stored procedure , i am getting an error :- "missing equal sign".
Even i tried doing it in a different way (commented code in proc) but that is also giving an error.
CREATE OR REPLACE PROCEDURE "MY_UPDATE_PROC"(update_values IN VCHAR2,myid IN INT)
sqlStmt VARCHAR2(1024);
BEGIN
UPDATE MY_TEST_TABLE SET update_values WHERE (TEST_Id = myid);
--sqlStmt := 'UPDATE MY_TEST_TABLE SET ' || update_values || ' WHERE TEST_Id = ' ||myid ;
-- EXECUTE sqlStmt;
END;
Try this (untested):
CREATE OR REPLACE PROCEDURE "MY_UPDATE_PROC"(update_values IN VARCHAR2, myid IN NUMBER) AS
sqlStmt VARCHAR2(1024);
BEGIN
sqlStmt := 'UPDATE MY_TEST_TABLE SET ' || update_values || ' WHERE TEST_Id = ' || myid;
EXECUTE IMMEDIATE sqlStmt;
END;
/
The datatype of your first parameter should be VARCHAR2 (maybe just a typo in your post)
Syntax of simple update statement in Oracle is:
Update <table_name>
set <column_name> = some_value
where <conditions..>
You update statement is missing = some_value part that you need to provide.
CREATE OR REPLACE PROCEDURE "MY_UPDATE_PROC"(P_update_values IN CHARVAR2, p_myid IN INT)
BEGIN
UPDATE MY_TEST_TABLE
SET col1 = p_update_values
WHERE TEST_Id = p_myid;
END;
/
Using Dynamic SQL, although not required in this case:
CREATE OR REPLACE PROCEDURE "MY_UPDATE_PROC"(p_update_values IN VARCHAR2, p_myid IN NUMBER) AS
sqlStmt VARCHAR2(1024);
BEGIN
sqlStmt := 'UPDATE MY_TEST_TABLE SET col1 = :a WHERE TEST_Id = :b';
EXECUTE IMMEDIATE sqlStmt USING p_update_values, p_myid;
END;
/
Things to be noted:
1) Always use meaningful and different names that are other than column names for the parameters.
2) Always use bind variables, :a and :b in above examples, to avoid SQL Injections and improve overall performance if you are going to call this procedure multiple times.
Related
I have a procedure which returns result set from query which is dynamically generated in oracle. It do returns the result set but what I want is to process from information from the generated result set and save it in a table.
This is my query..
PROCEDURE GetItem(pitem_list in varchar2,
PGetData OUT SYS_REFCURSOR)
is
strsql2 long;
BEGIN
strsql2 :='SELECT val, val1, val2 from table1'; ----- This is a sample query as the main query is complex so just for simplicity I wrote this here.
open PGetData for strsql2; ----- This do returns me the result set generated from the query;
Now what I want is to execute the query stored in "strsql2" variable and read the result and process some information..
I thought of executing it from FOR LOOP.
strsql2 :='SELECT val, val1, val2 from table1';
for log in (select strsql2 from dual) ---- I even tried execute immediate
loop
if(log.val = 'TEST')
then
insert into table ----
else
update table --
end if;
end loop;
open PGetData for strsql2; --- After saving the result in table then return the result set..
Where I m going wrong here, or is there any other way to do it?
I m stuck here.
In that case, you can simply fetch from the cursor into local variables. In this case, I know that my query returns three columns, one an integer, one a string, and one a date so I'm declaring three local variables to hold the results.
declare
l_sql varchar2(1000);
l_rc sys_refcursor;
l_integer pls_integer;
l_string varchar2(100);
l_date date;
begin
l_sql := q'{select 1 int, 'foo' str, date '2020-12-21' dt from dual}';
open l_rc for l_sql;
loop
fetch l_rc
into l_integer, l_string, l_date;
exit when l_rc%notfound;
dbms_output.put_line( 'l_string = ' || l_string ||
' l_integer = ' || l_integer ||
' l_date = ' || to_char( l_date, 'dd-mon-yyyy' ) );
end loop;
end;
/
A liveSQL link
I need to make a PL/SQL script.
The inputs are a schema name and a table name. How can I make it to a table name?
So e.g. I'd like to do this:
create or replace procedure proc(schema in varchar2, table in varchar2) is
begin
select * from 'schema.table';
end;
begin
proc('db', 'items');
end;
So I'd like to get everything from db.items.
I've tried concat, ( 'schema' || '.' || 'table'), put it in a variable, but non of these has worked.
What you need is dynamic sql. Example that will return and print the count of rows (you can change it accordingly to your needs):
SQL> set serveroutput on -- to be able to see the printed results.
SQL> create or replace procedure proc(p_schema in varchar2, p_table in varchar2) is
v_sql varchar2(100);
v_result number;
begin
v_sql := 'select count(*) from :1' || '.' || ':2';
EXECUTE IMMEDIATE v_sql into v_result USING p_schema, p_table;
DBMS_OUTPUT.PUT_LINE ('Total rows in table: '|| v_result );
end;
I need to build a process that creates tables dynamically.
I have this:
declare
type array is table of varchar2(30) index by binary_integer;
a array;
expression varchar2(2000);
RESUME_create LONG;
procedure createTables ( texto in VARCHAR2 ) is
begin
dbms_output.put_line('the parameter is: ' || texto);
expression := 'begin ' || texto || '; end;';
dbms_output.put_line(expression);
execute immediate expression;
end;
RESUME_create := 'CREATE TABLE RESUME (
R_Resume_date DATE DEFAULT SYSDATE NOT NULL ,
R_Resume_source CHAR(3) DEFAULT ''001'' NOT NULL ,
R_Resume_channel CHAR(3) DEFAULT ''001'' NOT NULL )';
createTables('RESUME_create');
end;
/
So this is just an example.
So imagine that I need to declare multiples CREATE TABLEs and call the createTable into a loop passing multiples string that the function has to evaluate and execute.
If I un understand well, you need to run a set of DDL statements stored in a collection. If so, you can use something like:
declare
type tArray is table of varchar2(1000) index by pls_integer;
vArray tArray ;
begin
vArray(1) := 'create table firstTab ( a number, b number)';
vArray(2) := 'create table secondTab ( c number, d varchar2(10))';
--
for i in vArray.first .. vArray.last loop
execute immediate vArray(i);
end loop;
end;
/
I have a table A which has column A which holds table names as values.
All these tables have a common column C. I need maximum value of this column for each table.
I tried this using dynamic SQL but I'm getting errors. Please suggest.
DECLARE
query1 VARCHAR2(100);
c_table VARCHAR2(40);
c_obj VARCHAR2(20);
Cursor cursor_a IS
SELECT a FROM A;
BEGIN
Open cursor_a;
LOOP
Fetch cursor_a INTO c_table2;
EXIT WHEN cursor_a%notfound;
query1 := 'SELECT max(object_ref) AS "c_obj" FROM c_table' ;
EXECUTE IMMEDIATE query1;
dbms_output.put_line('Maximum value: '|| c_table || c_obj);
END LOOP;
Close cursor_a;
END;
Dynamic SQL can't see your PL/SQL variable: you need to pass it a string which can be executed in the scope of the SQL engine. So you need to concatenate the table name with the statement's boilerplate text:
query1 := 'SELECT max(c) FROM ' || variable_name;
You also need to return the result of the query into a variable.
Here is how it works (I've stripped out some of the unnecessary code from your example):
DECLARE
c_table VARCHAR2(40);
c_obj VARCHAR2(20);
BEGIN
for lrec in ( select a as tab_name from A )
LOOP
EXECUTE IMMEDIATE 'SELECT max(object_ref) FROM ' || lrec.tab_name
into c_obj ;
dbms_output.put_line('Maximum value: '|| lrec.tab_name
|| '='|| c_obj);
END LOOP;
END;
There is some miss match in veriables that you had used i.e.
declared as "c_table" but accessing as "c_table2"
Each table common column name is "C" but accessing as "object_ref"
In dynamic query use INTO keyword to store the value to your varibale
Suggestions
Use concat() function to prepare the query dynamically i.e. something like:
SET #SQL := CONCAT('SELECT max(c) INTO ', c_obj, ' FROM ',c_table);
Steps of implementing dynamic query is:
SET #SQL = <your dynamic query>
PREPARE stmt FROM #SQL;
EXECUTE stmt;
Sample code:
DECLARE
query1 VARCHAR2(100);
c_table VARCHAR2(40);
c_obj VARCHAR2(20);
CURSOR cursor_a IS
SELECT a FROM A;
BEGIN
OPEN cursor_a;
LOOP
FETCH cursor_a INTO c_table;
EXIT WHEN cursor_a%notfound;
SET #SQL := CONCAT('SELECT max(object_ref) AS c_obj INTO ', c_obj, ' FROM ',c_table);
PREPARE stmt FROM #SQL;
EXECUTE stmt;
dbms_output.put_line('Maximum value: '|| c_table || c_obj);
END LOOP;
CLOSE cursor_a;
END;
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