Oracle returning result from dynamic SQL - oracle

Hi I am trying to return a value from Oracle using dynamic SQL. I am getting an error SQL command not properly ended. I can't figure out what I am doing wrong. Here is the latest code I've tried:
PROCEDURE get_record_counts
AS
v_EXT_RECCOUNT VARCHAR2(05) := '0';
BEGIN
EXECUTE IMMEDIATE 'select count(*) from ' || r_cls.EXT_TABLE || ' RETURN v_EXT_RECCOUNT into v_EXT_RECCOUNT ';
END get_record_counts;

You'd want
EXECUTE IMMEDIATE 'select count(*) from ' || r_cls.ext_table
INTO v_ext_reccount
assuming that r_cls.ext_table resolves to a varchar2 variable that contains a table name that the caller has appropriate permissions on. In the snippet you posted, that is not a valid variable name but I'm guessing that there is more code that you've removed that declares that variable.

Related

Oracle pl/sql Array

let's see if somebody can help me, I need to delete rows from different tables and I did think to do it using an array so i wrote this :
DECLARE
TYPE mytype_a IS TABLE OF VARCHAR2(32) INDEX BY BINARY_INTEGER;
mytype mytype_a;
BEGIN
mytype(mytype.count + 1) := 'MYTABLE';
FOR i IN 1 .. mytype.count
LOOP
DELETE mytype(i) WHERE valid = 'N';
END LOOP;
END;
Trying to run this piece of code using sqldeveloper I get the ORA-00933 command not properly ended, if I put directly the table name it works, what am I doing wrong?
Thank you.
Thank you very much guys, it works perfectly.
This is not the correct approach. You have to use Dynamic SQL for this -
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar(500) := NULL;
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid =''N''';
EXECUTE IMMEDIATE stmt;
end loop;
END;
You would need to use dynamic SQL, concatenating the table name from the collection into the statement, inside your loop:
execute immediate 'DELETE FROM ' || mytype(i) || ' where valid = ''N''';
Or you can put the statement into a variable so you can display it for debugging purposes, and then execute that, optionally with a bind variable for the valid value:
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
... which I've made also display how many rows were deleted from each table. Note though that you shoudln't rely on the caller being able to see anything printed with dbms_output - it's up to the client whether it shows it.
The whole anonymous block would then be:
DECLARE
type mytype_a is table of varchar2(32) index by binary_integer;
mytype mytype_a;
stmt varchar2(4000);
BEGIN
mytype (mytype.count + 1) := 'MYTABLE';
for i in 1..mytype.count loop
stmt := 'DELETE FROM ' || mytype(i) || ' where valid = :valid';
dbms_output.put_line(stmt);
execute immediate stmt using 'N';
dbms_output.put_line('Deleted ' || sql%rowcount || ' row(s)');
end loop;
END;
/
You could use a built-in collection type to simplify it even further.
db<>fiddle showing some options.
Hopefully this doesn't apply, but if you might have any tables with quoted identifiers then you would need to add quotes in the dynamic statement, e.g.:
stmt := 'DELETE FROM "' || mytype(i) || '" where valid = :valid';
... and make sure the table name values in your collection exactly match the names as they appear in the data dictionary (user_tables.table_name).

PL/SQL Using SELECT inside of CONDITION

I've installed Oracle Instance inside of docker container and I put sql script whitch runs when a container starts:
Dockerfile:
FROM...
...
CMD powershell echo #C:\scripts\entrypoint.sql | sqlplus -S sys/password as sysdba
entrypoint.sql:
set serveroutput on format wrapped;
declare
userexist integer;
db_schema_version varchar2(20);
begin
select count(*) into userexist from dba_users where username='DEV';
if (userexist = 0) then
dbms_output.put_line('SCHEMA NOT FOUND!');
dbms_output.put_line('CONNECTION STRING: ' || 'sqlplus sys/password#localhost/ORA193 as sysdba');
elsif (userexist = 1) then
dbms_output.put_line('SCHEMA FOUND!');
select DB_SCHEMA_VERSION into db_schema_version from DEV.VER_INFO where CODE = 'CORE';
dbms_output.put_line('DB_SCHEMA_VERSION: ' || db_schema_version);
dbms_output.put_line('CONNECTION STRING: ' || 'sqlplus DEV/password#localhost/ORA193');
end if;
end;
/
The issue is that query select DB_SCHEMA_VERSION into db_schema_version from DEV.VER_INFO where CODE = 'CORE'; is executing even if elsif condition is false
What is the correct way of writing PL/SQL to get select query executed only in case of condition is true?
The problem isn't that the query is being executed if the condition is false. The problem is that the PL/SQL block has to be compiled before it can be executed. And the compilation fails if it has a reference to an object that doesn't exist (if you posted the error message, it would be clear that it's a compilation error not a runtime error).
If you want to reference an object that might not exist, you'd want to use dynamic SQL. Something like
execute immediate 'select db_schema_version from dev.ver_info'
into db_schema_version;
would work in this case. In more complicated cases, you'd want to build the SQL statement in a local variable that you can log for debugging purposes or potentially use the dbms_sql package.

Oracle: Dynamic SQL with Update not working

I have this instruction
UPDATE TABLE1
SET INC =
(select INC from TABLE2
WHERE KEY = 'KEY_VALUE1'
FETCH FIRST 1 ROW ONLY);
This working fine if i run from sqlPlus or if I use in PLSQL but, if I using in Dynamic SQL, not working
sqlStmt:= 'UPDATE TABLE1'
|| 'SET INC = '
|| '(select INC from TABLE2 '
|| 'WHERE KEY = ''' || v_key_value || ''' '
|| 'FETCH FIRST 1 ROW ONLY); ';
BEGIN
EXECUTE IMMEDIATE sqlStmt;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('{"errorcode":"' || SQLERRM);
ROLLBACK;
END;
This instruction return this error:
{"errorcode":"ORA-00933: comando SQL terminato erroneamente
Someone can you help me?
Regards,
Marco
You don't want to have a semicolon at the end of the string you are building and passing to execute immediate.
It's not the cause of your error. But it would be much kinder to the database to write this using bind variables rather than concatenating literals. Of course, since there is no reason to be using dynamic SQL for this sort of update statement, I'm guessing your actual use case is different and that you are actually using bind variables and/or there is actually a reason why bind variables aren't an option.

DB Links as Parameters in a Function

I'm creating a function that accepts two parameters. And one of my parameter will serve as the database link for my statement. I've tried concatenating it. How will I be able to achieve this?
It shows this error
ORA-00923: FROM keyword not found where expected ORA-06512 at
"NOINK.CHECK_SECOND_REF_DIE", line 13.
Below is the code.
drop function check_second_ref_die;
create or replace function check_second_ref_die(lotNumber in VARCHAR2, db_link in VARCHAR2)
return varchar2
is
row_count NUMBER;
sql_statement VARCHAR2(300);
BEGIN
sql_statement := 'SELECT COUNT(*) FROM wcrepo.WCR_WAFER_REFERENCE#lepftds.itg.ti.com
WHERE waferconfigfile = (SELECT waferconfigfile FROM program_setup_rev#' || db_link ||
'WHERE device = (SELECT device FROM noink.lot WHERE lot_num = ' ||lotNumber || ')
AND setup_cnt=0) AND status =' || 'Approved' || 'AND ref_die_type =' || 'Secondary';
execute immediate sql_statement into row_count;
IF (row_count != 0) THEN
RETURN 'TRUE';
ELSE
RETURN'FALSE';
END IF;
END;
This is the code when I try to call the function
SELECT CASE
WHEN check_second_ref_die ('8019572', 'rfabtwdb.dal.make.ti.com') = 'TRUE'
THEN 'EXISTS' ELSE 'NOT EXISTS'
END
AS RESULT
FROM DUAL
AND status =' || 'Approved' || 'AND
This is wrong. Remove the concatenation operators and we have ...
AND status =ApprovedAND
... which is not valid SQL. To reference string literals you need to escape single quotes. The simplest way is to use two of them:
AND status =''Approved'' AND
You'll need to fix all the string literals in your code.
Dynamic SQL is hard because it turns compilation errors into runtime errors. You can make it easier to debug your code by including some simple instrumentation. If your code had this line before the EXECUTE IMMEDIATE you could have seen the executed statement and probably spotted the bloomer for yourself.
dbms_output.put_line(v_sql);

ORACLE: Cursor with dynamic query - throws error "invalid identifier" for cursor field

I have a logic to implement where I have to use dynamic sql(column names and where clause is decided on the fly).So here my cursor(emp_ref_cursor) has a dynamic sql, and has 3 cursor fields(emp_id,emp_name,dept).
Using these cursor fields in WHERE clause I am trying to execute another dynamic sql inside the loop.Bt oracle isn't able to identify the cursor field and throws an error like "ORA-00904: "EMP_REC"."EMP_ID": invalid identifier" though I am able to output emp_rec.emp_id through DBMS_OUTPUT.
NOTE: Please don't comment on the code quality this is not the actual code.
This is just used to describe the problem. I can't post the actual code due to
some compliance related stuff.
DECLARE
emp_ref_cursor sys_refcursor;
v_sql varchar2(3900);
TYPE emp_rec_type IS RECORD (emp_id number,emp_name varchar2(100),dept_id varchar2(100));
emp_rec emp_rec_type;
v_dept_id number:='1234';
v_dob varchar2(100);
v_desig varchar2(100);
x_dynamic_col_1 varchar2(100):='dob'; --dynamic column(based on some condition)
x_dynamic_col_2 varchar2(100):='designation'; --dynamic column(based on some condition)
x_dynamic_col_3 varchar2(100):='emp_id'; --dynamic column(based on some condition)
BEGIN
v_sql:='SELECT emp_id,emp_name,dept FROM employee WHERE dept_id=' || v_dept_id;
OPEN emp_ref_cursor FOR v_sql;
LOOP
FETCH emp_ref_cursor INTO emp_rec;
exit WHEN emp_ref_cursor%NOTFOUND;
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
DBMS_OUTPUT.PUT_LINE(stmt);
--Prints the SQL query as expected
DBMS_OUTPUT.PUT_LINE('emp_rec.emp_id:'||emp_rec.emp_id);
--Displays the value!!!
execute immediate stmt into v_dob, v_desig;
--But why is it saying emp_rec.emp_id is invalid identifier??
END LOOP;
END;
You have emp_rec defined as a local PL/SQL variable. None of the PL/SQL data is in scope to the dynamic SQL execution. When it is executed it as if you tried to run the statement - as it is displayed by your dbms_output standalone in a separate SQL context. If you did that it would be clear that emp_rec doesn't exist to the query.
You refer to it you would need to use a bind variable:
WHERE emp_id=:dynamic_col_3';
And then execute it with:
execute immediate stmt using emp_rec.emp_id;
But you can't use the x_dynamic_col_3 local variable in the using clause. Since - in this example anyway - the query would also need to change to use a different table column is the dynamic record field changed - that doesn't seem too much of a problem. But you said the where clause will change on the fly too. In that case you could have another local variable that you set to the relevant x field before the executin.
You have incorrect using of EXECUTE IMMEDIATE. You don't need to put INTO clause to SQL query. Use this instead:
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
execute immediate stmt into v_dob, v_desig;

Resources