execute immediate oracle string concatenate issue - oracle

I am having issues concatenating a string to be called be execute immediate. Anybody have any idea what is wrong with the following statement below.
EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server = ''||SMTPServer||''';
select smtpserver into SMTPServer from mytable;
if(SMTPServer is not null)
then
EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server = ''||SMTPServer||''';
--*****above does not work, below does work****
EXECUTE IMMEDIATE 'ALTER SESSION SET smtp_out_server = ''10.1.1.1''';

From the standpoint of building the string you want, you're missing a couple of single quotes. You'd need 3 single quotes before and four single quotes after the SMTPServer local variable
'ALTER SESSION SET smtp_out_server = ''' || smtpServer || ''''
Personally, I'd find it easier to do something like this where you declare a template (using the q quoting syntax since it has embedded single quotes), call replace to replace the templated values, and then call EXECUTE IMMEDIATE. It's a bit more code but it generally makes life easier.
l_sql_stmt := q'{ALTER SESSION SET smtp_out_server = '##server##'}';
l_sql_stmt := replace( l_sql_stmt, '##server##', smtpServer );
EXECUTE IMMEDIATE l_sql_stmt;
Are you sure that the second statement really works, though? smtp_out_server isn't supposed to be modifiable without a database restart. I've seen folks say that they've been able to modify it successfully with an ALTER SYSTEM at runtime but haven't seen anyone try to do it with an ALTER SESSION. I'd hesitate to build code that depends on behavior that goes against the documentation. If you need to control the SMTP server you're using at runtime, I'd strongly suggest using the utl_smtp package rather than utl_mail.

It's not really good providing the statement for EXECUTE IMMEDIATE replacing the parameters, since for the optmizer it's a completely different statement every single time so it prepares another execution plan for each.
You should use binding in your EXECUTE IMMEDIATE clause.
Example:
DECLARE
SMTPServer mytable.smtpserver%type;
l_set_smtpserver_stmt VARCHAR2(600) := q'#ALTER SESSION SET smtp_out_server = ':p_smtp_server'#'
BEGIN
SELECT smtpserver INTO SMTPServer FROM mytable;
IF SMTPServer IS NOT NULL THEN
EXECUTE IMMEDIATE l_set_smtpserver_stmt USING SMTPServer;
END IF;
EXCEPTION
-- handle no_data_found exception since you're using select .. into statement
WHEN NO_DATA_FOUND THEN
-- handle exception
WHEN OTHERS THEN
-- handle exception
END;
/

Related

How to use this execute immediate create table to insert values in table

I am using execute immediate to create table and use this table into my code to insert some values when i run this code .it gives me error that table and view does not exist.why?. this code has ability to create table because when i use drop and create table command with existing table by using execute immediate then my code works fine and when i just remove drop table syntax, my code does not work, Please help me to clear my concept about dynamic SQL-----------
SET serveroutput ON
DECLARE
ddl_table VARCHAR2(200);
r_emp SYS.ODCINUMBERLIST := SYS.ODCINUMBERLIST();
v_array SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST('ACCT_ID',
'PARENT_ACCT_ID',
'CUST_ID',
'ACCT_NAME',
'BILLING_CYCLE_TYPE',
'PAID_FLAG',
'BILL_DELIVER_METHOD');
BEGIN
ddl_table := 'CREATE TABLE test123(
v_column VARCHAR2(50),
v_count NUMBER
)';
EXECUTE IMMEDIATE ddl_table;
DBMS_OUTPUT.ENABLE;
FOR i IN 1 .. v_array.COUNT LOOP
r_emp.EXTEND;
EXECUTE IMMEDIATE 'SELECT COUNT(*)
FROM account_profile
WHERE NOT REGEXP_LIKE(' ||v_array(i) || ',''[A-Za-z0-9.]'')'
INTO r_emp(i);
IF r_emp(i) <> 0 THEN
DBMS_OUTPUT.PUT_LINE(v_array(i) || ': ' || r_emp(i));
INSERT INTO test123 VALUES (v_array(i), r_emp(i));
END IF;
END LOOP;
END;
/
Error report -
ORA-06550: line 24, column 17:
PL/SQL: ORA-00942: table or view does not exist
Your problem is that the annonymous block is pre-validated (pre-compiled) before it is valid. Oracle will check all objects in use before executing it. Since you are creating test123 dynamically it doesn't exist so your insert statement fails.
You can instead, use an EXECUTE IMMEDIATE command to also insert the data on your test123 table.
The way you use the EXECUTE IMMEDIATE for an insert command is either concatenating the parameters or preparing them, I prefer the later. Like this
execute immediate 'insert into test123 values (:param1,:param2)' using v_array(i), r_emp(i);
Here is the official documentation for the EXECUTE IMMEDIATE command.
Though it shows how it works and explain the usage of it, it doesn't particularly answer you direct question on the comments.
So
can you explain :param1,:param2
Those are called "binding" parameters that will be replaced by the variables used after the using statement. It doesn't matter their name only the order in which they appear on the string. The number of parameters within the dynamic string needs to match the number of parameters after the using statement.
why we use it with : colon and what are these
The colon : there is just to make it easier for the parser to know what to replace and where when using the variables you provided after the using statement
Translating that execute immediate it would be something like this:
... values (:param1,:param2)' using v_array(i), r_emp(i)
Hey "execute immediate" whenever you see :param1 please replace it with the content of the variable I'm providing as v_array(i) and whenever you see :param2 please replace it with the content of the variable I'm providing as r_emp(i)

Is SQL statement in EXECUTE IMMEDIATE parsed at run time?

Considering below procedure. For processing, it has to go through all the processing phases anyway. So my question is: Is it at this parse phase 'Select job_id from asfd' still treated as string and while the execution phase of EXECUTE IMMEDIATE the statement 'Select job_id from asfd' is parsed again now as SQL statement?
CREATE OR REPLACE PROCEDURE test_ei
IS
BEGIN
EXECUTE IMMEDIATE ‘select job_id from asfd’; //line 3
END;
One explanation I found in here is the asdf table does not exist but gut successfully compiled, so 'Select job_id from asfd' is still treated as string at this stage. Any corrections please.
As matter of fact even when EXECUTE IMMEDIATE 'select * asfd'; is replace in line 3 it still complies successfully. So I guess above explanation is correct.
Yes. At EXECUTE IMMEDIATE, the runtime engine must first parse the statement.And i.e the reason why you cannot bind in the names of schema elements (tables, columns, etc.)
More details:
The parse phase is what guarantees that the SQL statement is properly defined. PL/SQL can tell that the following statement is valid without having to know the value of :xyz.
'UPDATE emp SET sal = :xyz'
But how can PL/SQL know if the following statement is well formed? We can't, and so the restriction.
'UPDATE emp SET :col_name = :xyz'

PL/SQL UPDATE/INSERT with table's variable

It is possible to build a statement using variables for the table name without creating a VARCHAR and using EXECUTE IMMEDIATE?
ex:
#table_name := test_table
UPDATE #table_name
SET col1 = val1
WHERE condition...;
this is my actual workaround:
DECLARE
sql_stmt VARCHAR2(1000);
table_name VARCHAR2(30) := 'test_table';
BEGIN
sql_stmt := 'UPDATE ' || table_name || ' SET col1 = val1...';
EXECUTE IMMEDIATE sql_stmt;
END;
/
"[Is it] possible to build a statement using variables for the table
name without creating a VARCHAR and using EXECUTE IMMEDIATE?"
No.
SQL is a strongly typed language. The compiler requires the actual table name in order to validate the various components of the SQL statement ( projection, filters, etc).
Dynamic SQL (EXECUTE IMMEDIATE and DBMS_SQL) is necessary to short-circuit the compile time validation. Essentially it's a mechanism relieving the compiler of the burden of verifying the SQL and taking it on ourselves (because humans are so much better at such jobs than machines. Not).
Alternatively, it's a mechanism for generating runtime errors instead of compilation errors.
Either way, it's pretty odd to know the name of the COLUMN in the filter but not the name of the TABLE being updated.

Defining variable for repeated use in PL/SQL anonymous blocks

I am using Oracle SQL Developer.
I have an anonymous block that drops a table if it exists.
I don't have permission to create procedures or functions, so I have to call the anonymous block repeatedly.
To simplify things, I would like to store the names of all the affected tables in variables at the beginning of my script, and then refer to the appropriate variables later.
DEFINE v_InputTable = 'Table Name';
DECLARE
InputTable VARCHAR2(80) := &v_InputTable;
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || InputTable;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
When I try this, I get an error "PLS-00103: Encountered the symbol "TABLE" when expecting one of the following:..."
Can someone please explain what I am doing wrong.
Substitution variables are replaced in a fairly simplistic way. You have to enclose your references to strings within single quotes, where they become a literal. Instead of what you have, that becomes:
DECLARE
InputTable VARCHAR2(80) := '&v_InputTable';
You don't really need the PL/SQL variable here, you can use the substitution variable directly in the dynamic statement (still within a quoted string):
DEFINE v_InputTable = 'Table Name';
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE &v_InputTable';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
/
You can also define a bind variable with the variable command, rather than define, but for this usage a substitution is probably simpler.
You could run that drop comand statically as plain SQL of course, but I guess you want to hide the table-not-found error.
The error suggests your defined value is actually literally 'Table Name', which is an invalid name anyway unless you treat it as a quoted identifier - and it's really better not to do that. But if you must, or are playing around to see how they work, remember the case is fixed too, and every reference to it has to be identical and quoted. If you are trying to drop (and then recreate?) a table with a space in the name, you'd need to wrap the value in double quotes:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE "&v_InputTable"';
... or if you want a separate variable and concatenate the dynamic statement:
DECLARE
InputTable VARCHAR2(80) := '"&v_InputTable"';

Testing native dynamic SQL in Oracle before execution

I have implemented a certain feature in my application where the user can compose queries dynamically from the user interface by pushing around buttons and inserting some values here and there.
The user will not see the generated SQL statement at all.
I was wondering if there is a way to perhaps check the syntax and grammar (e.g. he opened a parantheses '(' and forgot to close it ) of the dynamically generated SQL to ensure that no run-time compilation errors would happen before actually executing the statement using EXECUTE IMMEDIATE.
You could use the dbms_sql.parse procedure to parse the SQL statement assuming the statement is DML not DDL. It would be rather unusual to parse a dynamic SQL statement using the dbms_sql package and then use EXECUTE IMMEDIATE to execute it rather than using dbms_sql.execute but nothing prevents you from mixing dbms_sql and execute immediate.
The code to just parse the SQL statement would be something like
DECLARE
l_cursor integer;
l_sql_stmt varchar2(1000) := <<some SQL statement>>;
BEGIN
l_cursor := dbms_sql.open_cursor;
dbms_sql.parse( l_cursor, l_sql_stmt, dbms_sql.native );
dbms_sql.close_cursor( l_cursor );
END;
You can also use explain plan, this is basically doing the first step of the execution.
SQL> explain plan for select * from dual;
Explained.
SQL is valid, you can also use the explain tables to get the tables, views etc, maybe even estimated run time ...
SQL> explain plan for select * from duall;
explain plan for select * from duall
*
ERROR at line 1:
ORA-00942: table or view does not exist
SQL is invalid, this is why ...
you can also use it in a dynamic statement
SQL> begin execute immediate 'explain plan for ' || ' select * from dual'; end;
2 /
PL/SQL procedure successfully completed.
As always use error-handling, e.g. write a little error log creater package or procedure what u call when it's needed.
DECLARE
my_stmt VARCHAR2(4000):='this will contain my statements';
BEGIN
'begin' || my_stmt || 'end';
EXCEPTION
WHEN OTHERS THEN
prc_my_error_log_creator();
END;
in the log_creator use for example the DBMS_UTILITY.FORMAT_ERROR_BACKTRACE() func to get the stack's contain.
Regards

Resources