invalid identifier error while using NVL in EXECUTE IMMEDIATE SELECT - oracle

create or replace PROCEDURE P_STAGE_LOAD(
in_S IN VARCHAR2,
in_D IN VARCHAR2,
in_T IN VARCHAR2)
IS
v_ERRM VARCHAR2(32000);
BEGIN
IF in_TBL_NAME ='TRAN_CUSTOMER' THEN
EXECUTE immediate 'INSERT INTO TRAN_CUSTOMER
(CUSTOMER_ID,AUTH_ID,OTHER_ID,TRAN_TYPE,AUDIT_TS)
SELECT CUSTOMER_ID,AUTH_ID,OTHER_ID,NVL(TRAN_TYPE,'PRIMARY'),
SYSDATE
FROM '||in_S||'.'||in_T||'#'||in_D;
EXECUTE immediate 'COMMIT';
This gives error as Invalid Identifier in run time. However the SELECT query runs fine alone.
I want to handle NULL of TRAN_TYPE, and decode to get as PRIMARY when NULL.
The target table TRAN_CUSTOMER has this as NOT NULL Column.
Is there anything missing?

You need to use two single quotes for PRIMARY as first single quote is ending the EXECUTE IMMEDIATE string.
Use something like below in that line.
EXECUTE immediate 'INSERT INTO TRAN_CUSTOMER
(CUSTOMER_ID,AUTH_ID,OTHER_ID,TRAN_TYPE,AUDIT_TS)
SELECT CUSTOMER_ID,AUTH_ID,OTHER_ID,NVL(TRAN_TYPE,''PRIMARY''),
SYSDATE
FROM '||in_S||'.'||in_T||'#'||in_D;

Related

Oracle: Using EXECUTE IMMEDIATE with RETURNING INTO

I have a procedure that does a validation and inserts a record in a table. The procedure is breaking right after the INSERT statement when I try the following code:
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' returning into V_TRGT_ID;
I am trying to execute my INSERT statement which is stored in V_SOME_STRNG and assign the new record's ID to V_TRGT_ID. However, I am running into the following error:
ORA-00933: SQL command not properly ended
Any thoughts?
You don't need to repeat the returning into part, you need a using clause for your bind variable:
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' using out V_TRGT_ID;
Demo using a basic trigger to provide the ID:
create table t42 (some_id number, dummy varchar2(1));
create sequence s42 start with 42;
create trigger tr42 before insert on t42 for each row
begin
:new.some_id := s42.nextval;
end;
/
set serveroutput on
declare
v_some_strng varchar2(200) := 'insert into t42 (dummy) values (''X'')';
v_trgt_id number;
begin
EXECUTE IMMEDIATE V_SOME_STRNG || ' returning SOME_ID into :NEW_ID' using out V_TRGT_ID;
dbms_output.put_line('Returned ID: ' || v_trgt_id);
end;
/
which shows:
Returned ID: 42
PL/SQL procedure successfully completed.
You can only use returning into with the insert .. values ... pattern, not with insert ... select ...; so for instance changing the code above to use;
v_some_strng varchar2(200) := 'insert into t42 (dummy) select ''X'' from dual';
will get the error you originally reported:
ORA-00933: SQL command not properly ended
ORA-06512: at line 6
While you don't need to use returning into part, the OP problem most likely results from an error in the not shown content of the V_SOME_STRNG variable. Because you definitely can use returning into with execute immediate. Here is an example strait from the documentation:
sql_stmt := 'UPDATE emp SET sal = 2000 WHERE empno = :1 RETURNING sal INTO :2';
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
I stress the point again: it works. So if you have any troubles here check you dynamically generated SQL statement more thoroughly.
My test_queries table consist of 2 columns:fid and query_text.I want insert new row. And I return fid I inserted because I use it next question. But the code give me error .
select max(a.fid) into max_fid from test_queries a;
execute immediate 'insert into test_queries values (:1,:2) returning fid into :a' using max_fid+1,query_text,c;

PL/SQL Concatenation

i have a procedure that have in entry two parameters
CREATE_PARTITION( yearSource IN VARCHAR2 , yearDestination IN VARCHAR2 )
when i want to insert the yearSource concatenated with another string , nothing is inserted in the table
i declare the variable yearAA which take the yearSource and i concatenate it with a 'AA'
yearAA varchar2(30) := yearSource||'AA';
to insert it i use :
execute immediate ' INSERT INTO MOUADTEST2018 VALUES('||yearAA||')';
result : Nothing inserted
and it shows this message ORA-00984: column not allowed here
You have to quote the value. As it is, if yearAA is 2018AA, your code gets interpreted as:
execute immediate 'INSERT INTO MOUADTEST2018 VALUES(2018AA)';
Which tries to run
INSERT INTO MOUADTEST2018 VALUES(2018AA)
Which throws an error because 2018AA isn't in quotes, so Oracle thinks it must be an identifier (like a column name).
You could change your code to
execute immediate 'INSERT INTO MOUADTEST2018 VALUES('''||yearAA||''')';
But this isn't a best practice, since it allows SQL injection. Instead, use bind variables.
execute immediate 'INSERT INTO MOUADTEST2018 VALUES(:1)' using yearAA;
Also, I don't think you need execute immediate, so you can just do:
insert into MOUADTEST2018 values(yearAA);

Invalid table name error, when execute alter stored procedure in oracle

-- Disable constaint, good
CREATE OR REPLACE PROCEDURE cpl_disable_constraint(table_name IN varchar2, constraint_name IN varchar2)
AS
BEGIN
execute immediate 'ALTER TABLE :1 DISABLE CONSTRAINT :2' using table_name, constraint_name;
END;
/
-- Bug
declare
table_name varchar2(100) := 'ADV_TEST_COURSE_CREDIT';
column_name varchar2(100) := 'SEQUENCE_NUMBER';
begin
cpl_disable_constraint(table_name, column_name);
end;
/
I'm getting these errors:
ORA-00903: invalid table name
ORA-06512: at "SISD_OWNER.CPL_DISABLE_CONSTRAINT", line 5
ORA-06512: at line 5
00000 - "invalid table name"
Any idea?
As a_horse_with_no_name mentioned, you can't pass an identifier to execute immediate as a parameter. You have to put it in the SQL statement.
execute immediate 'ALTER TABLE ' || table_name || ' DISABLE CONSTRAINT :1' using constraint_name;
Note that this will open you up to SQL Injection if you don't carefully validate the table_name variable. I usually do something like this before calling execute immediate, to make sure the table_name variable is the valid and correct table name for the constraint, and not some malicious string like null; DROP TABLE ADV_TEST_COURSE_CREDIT;:
select c.table_name into table_name from user_constraints c where c.constraint_name = constraint_name;
(By the way, this is part of why people often choose a prefix for local PL/SQL variable names, like "v_table_name", to keep them separate from column names. You can see it's a little confusing in the query above.)
Also I'd like to point out that in your function definition, you're calling the second parameter "constraint_name", but in the anonymous block you're calling it "column_name".

Use execute immediate in procedure for DML and passing character value in parameter

I have a delete procedure which is taking table name and some values to delete record from that table, hence I have created a procedure with execute immediate which is forming the delete query by taking the parameter and delete.
But when ever I am passing the char value in the parameter it is getting error :
invalid identifier
as query formed with out single quote for the character value. Please let me know how can I pass char value in the procedure to form a string correctly.
Below is the procedure:
CREATE OR replace PROCEDURE Prd_delete(p_tbl_name IN VARCHAR2,
p_sys VARCHAR2,
p_b_id VARCHAR2,
p_c_date NUMBER)
IS
dlt_query VARCHAR2(200);
BEGIN
dlt_query := 'delete from '
||p_tbl_name
||' where system='
||p_sys
|| ' And batch_id='
||p_b_id
|| ' And cobdate='
||p_c_date;
--dbms_output.put_line(dlt_query);
EXECUTE IMMEDIATE dlt_query;
END;
/
Below is the running command :
exec prd_delete ('TBL_HIST_DATA','M','N1',20141205);
Below is the error :
ORA-00904:"N1" invalid identifier.
How to pass this value correctly ? please suggest.
At first place, why do you need PL/SQL for the DELETE. You could do it in plain SQL.
Why is P_C_DATE a NUMBER, What data type is cobdate COLUMN. A date should always be a DATE. If the column data type is DATE, then you will run into more errors. Always pay attention to declaring correct data types.
With dynamic SQL, before directly executing, it is always a good practice to see whether the query is formed correctly using DBMS_OUTPUT. I would also suggest to use quoting string literal technique to make it even easier.
DBMS_OUTPUT.PUT_LINE(dlt_query);
The issue with the query is that you are missing the single-quotation marks around the VARCHAR2 type.
Modify the query to -
dlt_query := 'delete from '||P_TBL_NAME||' where system='||P_SYS||
' And batch_id='||''''||P_B_ID|| '''' ||
' And cobdate='||P_C_DATE;
you are losing the quotes around N1 during concatination
you can fix by adding quotes before and after , eg.
dlt_query := 'delete from '||P_TBL_NAME||' where system='||P_SYS||
' And batch_id='||''''||P_B_ID|| '''' ||
' And cobdate='||P_C_DATE;
If you have to use the EXECUTE IMMEDIATE statement, you should use bind variables:
CREATE OR REPLACE PROCEDURE prd_delete (P_TBL_NAME IN VARCHAR2,
P_SYS VARCHAR2,
P_B_ID VARCHAR2,
P_C_DATE NUMBER) IS
dlt_query VARCHAR2 (200);
BEGIN
dlt_query := 'delete from ' || P_TBL_NAME || ' where system=:1 and batch_id=:2 and cobdate=:3';
BEGIN
EXECUTE IMMEDIATE dlt_query USING P_SYS, P_B_ID, P_C_DATE;
EXCEPTION
WHEN OTHERS THEN
-- catch exception !!
END;
END;
/

Dynamic SQL and out field in oracle procedure

I get error when I use this:
PROCEDURE GET_BY_CRIT(vchFilter varchar2(500),
intCantTotal OUT INT,
curResult OUT sys_refcursor)
IS
BEGIN
OPEN curResult FOR
'SELECT COLUMN1,COLUMN2 FROM SOME_TABLE WHERE '||vchFilter
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM SOME_TABLE WHERE '||vchFilter
INTO intCantTotal
END
Error:
ORA-00936: missed expression
But when I execute each sentence by separate it run correcly.
The error you're getting doesn't seem to make sense. Oracle should be throwing a compilation error because parameters to functions don't have a length. vchFilter should be declared as a VARCHAR2, not a VARCHAR2(500).
Additionally, as Lolo pointed out in the comments, statements in a PL/SQL block need to be terminated with semicolons.
PROCEDURE GET_BY_CRIT(vchFilter varchar2,
intCantTotal OUT integer,
curResult OUT sys_refcursor)
IS
BEGIN
OPEN curResult FOR
'SELECT COLUMN1,COLUMN2 FROM SOME_TABLE WHERE '||vchFilter;
EXECUTE IMMEDIATE 'SELECT COUNT(*) FROM SOME_TABLE WHERE '||vchFilter
INTO intCantTotal;
END;
Be aware as well that there is no guarantee that second SQL statement will see the same COUNT that the first SQL statement did unless you can guarantee that SOME_TABLE is not being modified by any other sessions at the same time you're querying it. I'd generally be rather wary of a need to run a query and execute a separate count-- that generally indicates a more basic problem. If you need the COUNT to be consistent with the query you're running, you'd want to add an analytic COUNT to your query and let the caller fetch that column.
PROCEDURE GET_BY_CRIT(vchFilter varchar2,
curResult OUT sys_refcursor)
IS
BEGIN
OPEN curResult FOR
'SELECT COLUMN1,COLUMN2, COUNT(*) OVER () cnt FROM SOME_TABLE WHERE '||vchFilter;
END;

Resources