I am working on a procedure to transpose data from a large matrix into a table consisting of three columns. I'm having some difficulty dynamically inserting rows into the table. When I try to execute the procedure block below, I get an error mesage:
ORA-00936: missing expression
ORA-06512: at line 24
00936. 00000 - "missing expression"
The procedure generates a valid insert statement, that I can copy and execute as static SQL. Everything up to execute immediate stmnt is working properly. Moreover, I have a nearly identical procedure that functions perfectly. There is only one difference between the two. In the working version, all of the values inserted are of type "VARCHAR2". I'm at a loss as to how to continue troubleshooting.
declare
type rec_type is record(
row_name varchar2(250),
measurement number(30,27)
);
my_rec rec_type;
type cols_type is table of varchar2(10);
cols cols_type;
stmnt varchar2(2000);
cur sys_refcursor;
begin
select colnames bulk collect into cols from p100_stg1_tmnt_meta;
for i in cols.first..cols.last loop
stmnt := 'select site_id, '|| cols(i) ||' from p100_stg1_site_matrix';
open cur for stmnt;
loop
fetch cur into my_rec;
exit when cur%notfound;
stmnt := 'insert into p100_stg1_site_measurement (site_id, col_name, measurement) values '||
'('''||my_rec.row_name ||''', '''||cols(i)||''', '||my_rec.measurement||')';
--dbms_output.put_line(stmnt);
execute immediate stmnt;
end loop;
end loop;
end;
/
An example of an insert statement generated by the above procedure:
insert into p100_stg1_site_measurement (
site_id,
col_name,
measurement
)
values (
'5715_C17orf85_S500_RPHS[+80]PEKAFSSNPVVR',
'tmnt_2',
.0288709682691077
)
Environment:
SQL Developer on Ubuntu 16.04
Oracle 12c Community Edition.
You should use bind variables, i.e.
stmnt := 'insert into p100_stg1_site_measurement (site_id, col_name, measurement)
values (:site_id, :col, :measurement)';
execute immediate stmnt using my_rec.row_name, cols(i), my_rec.measurement;
Related
Insert statement below holds details of another Table that has 3 columns (id,ins_dt,text_stuff)
INSERT INTO swpurge_config
(schema
,table_name
,table_alias
,driving_column
,deletion_predicate
,retention_period_type
,retention_period_value)
VALUES
('CUSTOMERS_OWNER'
,'LAST_NAMES'
,'LN'
,'ins_dt'
,'WHERE ln.ins_dt < SYSDATE - p_retention_period_value
AND ora_hash(ln.rowid, 8) = 1'
,'month'
,'6');
Aim:
I am essentially trying to add a delete predicate in a varchar2 column. The idea is to call this column in a Procedure which will delete records up to 1000 rows:
PROCEDURE delete_rows
(
p_schema IN VARCHAR2
,p_table_name IN VARCHAR2
,p_table_alias in varchar2
,p_retention_period_value IN VARCHAR2
,p_delete_predicate IN VARCHAR2
) IS
v_sql varchar2 (32000);
v_row_limit pls_integer (1000);
BEGIN
v_sql := 'delete from ' || p_schema ||'.'|| table_name ||p_table_alias
'older than '|| p_retention_period_value || p_delete_predicate;
dbms_output.put_line(v_sql);
END delete_rows;
Not sure about 2 things:
1. How to store the sql where clause in a table column?
2. How to execute the where clause as a statement in the procedure?
Thanks
you are talking about Dynamic SQL.
You can just store it inside VARCHAR2 in string format. then retrieve it
select deletion_predicate into v_sql from swpurge_config where ...
then
v_sql := "SELECT ...... FRom .... " || v_sql;
finally execute it
EXECUTE IMMEDIATE v_sql;
if your sql statement also includes parameters then
EXECUTE IMMEDIATE query_str
using param1;
I have a table where i have to update multiple records on one button click. I am trying to update multiple record using below simple query.
UPDATE tablename SET column1=1 WHERE
idcolumn IN ('1','2','3')
where datatype of idcolumn is Number. If i run this query manually its working perfectly. But if i pass these ('1','2','3') parameteres through procedure then it is showing me below error i.e. (ora-01722 invalid number).
I tried to_number() function but still it is showing me above error.
Proc:
CREATE OR REPLACE PROCEDURE procname(idpara VARCHAR2,
RCT_OUT OUT SYS_REFCURSOR) IS
BEGIN
UPDATE tablename SET column1 = 1 WHERE idcolumn IN (idpara);
COMMIT;
OPEN RCT_OUT FOR
SELECT 'RECORD UPDATED SUCCESSFULLY' RESULT FROM DUAL;
END;
The procedure does not understand IN (idpara) with idpara being '1','2','3' as IN ('1','2','3') but as IN (q'!'1','2','3'!'). In other words, it is not searching for '1' and '2' and '3' but for '1,2,3'. But while '1' can be converted to a number '1,2,3' can not.
Here is a test case for you to show you:
select * from dual;
-- X
-- notice I have 'X' in the in list below
set serveroutput on
declare
idpara varchar2(400) := q'!'X','2','3'!';
v_out varchar2(400);
begin
select count(*) into v_out from dual where dummy in (idpara);
dbms_output.put_line(v_out);
end;
/
-- 0
declare
idpara varchar2(400) := q'!'X','2','3'!';
v_out varchar2(400);
sql_stmt VARCHAR2(1000) := NULL;
begin
sql_stmt :='select count(*) from dual where dummy in ('||idpara||')';
execute immediate sql_stmt into v_out;
dbms_output.put_line(v_out);
end;
/
-- 1
One solution inside of procname would be to build a pl/sql object of numbers and use that in the update. There is a lot of info out there on how to do it. E.g. here Convert comma separated string to array in PL/SQL And here is info on how to use the object in the IN-clause Array in IN() clause oracle PLSQL
Here is What i actually wanted to do, Fetch Data From a Table without knowing any columns but i.j.Column_Name gives an error, so i wanted to put it in a variable. After reading the comments i think it's not possible
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
CURSOR C2 IS SELECT Table_Name,Column_Name FROM user_tab_columns
WHERE data_type='VARCHAR2';
v_table Varchar2(256);
v_Col varchar2(200);
BEGIN
FOR i in C1 LOOP
FOR j in (SELECT Column_Name FROM user_tab_columns WHERE
Table_Name='Table_Name') LOOP
dbms_output.put_line(i.j.Column_Name);
END LOOP;
END LOOP;
END;
/
No, There is no Column Named v_Col
You can't refer to a field in a record (which is what the cursor loop is giving you) dynamically. If you need to do flexibly then you can use dbms_sql (possibly adapting this approach), but in the scenario you've shown you could use dynamic SQl to only get the column you want in the cursor:
-- dummy data
create table table_name (id number, column_name varchar2(10), other_col date);
insert into table_name values (1, 'Test 1', sysdate);
insert into table_name values (2, 'Test 2', sysdate);
DECLARE
CURSOR C1 IS SELECT * FROM Table_Name;
v_Cur sys_refcursor;
v_Col varchar2(200);
v_Val varchar2(4000);
BEGIN
v_Col:= 'Column_Name';
OPEN v_Cur for 'SELECT ' || v_Col || ' FROM Table_Name';
LOOP
FETCH v_Cur INTO v_Val;
EXIT WHEN v_Cur%notfound;
dbms_output.put_line(v_val);
END LOOP;
END;
/
Test 1
Test 2
PL/SQL procedure successfully completed.
The downside of this is that whatever the data type of the target column is, you have to implicitly convert it to a string; but you would be doing that in the dbms_output call anyway. So if you change the column you want to print:
v_Col:= 'Other_Col';
then the output from my dummy data would be:
2018-08-23
2018-08-23
PL/SQL procedure successfully completed.
where the date value is being implicitly formatted as a string using my current NLS session settings.
You could get more advanced by checking the data type in user_tab_columns and changing the dynamic query and/or the fetch and handling, but it isn't clear what you really need to do.
I am trying to create a procedure that accepts two table names as parameters, and then copies the rows from one table into another, one at a time. I know bulk insert and SELECT INTO are better ways to do this, but bulk insert isn't working because the table triggers throw mutation errors when I insert more than one row at once.
I've seen other answers that recommend using dynamic SQL, but I'm stuck on how to define the cursor that way.
CREATE OR REPLACE PROCEDURE TABLE_INSERT(
donor_t IN VARCHAR2,
empty_t IN VARCHAR2
)
AS
CURSOR C1 IS
SELECT * FROM donor_t;
BEGIN
FOR row IN C1
LOOP
INSERT INTO empty_t VALUES row;
END LOOP;
END;
When compiled as written above, compiler throws ORA-00942: table or view does not exist. When compiled with table names hard-coded in, this function inserts the rows as expected, without errors.
try this instead:
CREATE OR REPLACE PROCEDURE TABLE_INSERT(
donor_t IN VARCHAR2,
empty_t IN VARCHAR2
)
AS
V_SQL VARCHAR2(1000);
BEGIN
V_SQL := 'INSERT INTO ' || empty_t || ' SELECT * FROM ' || donor_t;
EXECUTE IMMEDIATE V_SQL;
END;
/
I am working on Oracle stored procedures.
My requirement is below
IF variable1 := 'true"
THEN
tableName=abr
ELSE
tableName=mvr
END IF;
FOR i IN (select unique(row1) as sc from tableName t where t.row2 = 'name') LOOP
BEGIN
-- required Logic
END
END LOOP;
But here I am not able to pass the table name in tableName parameter. How to do it?
You'll need to use Execute Immediate - it's designed for operations that aren't known until run time.
For normal operations, Oracle must know the tables and columns at compile time. You can't do SELECT * FROM tableName because it has no idea what tableName is and therefore it can't be compiled correctly.
Instead, you can do EXECUTE IMMEDIATE 'SELECT * FROM ' || tableName;
You can select your results INTO a variable, loop the result set, or BULK COLLECT into a structure and then iterate that.
For a simple select into, you can do this:
EXECUTE IMMEDIATE 'SELECT COL1, COL2 FROM ' || tableName INTO V_COL1, V_COL2
V_COL1 & V_COL2 are just local variables, tableName is a string representing your table name, and COL2 and COL2 are columns in the table you're selecting from. You can use the likes of ALL_TAB_COLUMNS to get the structure of a table dynamically.
Here is an example from Oracle docs:
CREATE OR REPLACE PROCEDURE query_invoice(
month VARCHAR2,
year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := 'SELECT num, cust, amt FROM inv_' || month ||'_'|| year
|| ' WHERE invnum = :id';
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/
http://docs.oracle.com/cd/B12037_01/appdev.101/b10795/adfns_dy.htm
You are going to have to build a for loop for each table then use your logic to determine which loop you will execute.