Oracle: DBMS_UTILITY.EXEC_DDL_STATEMENT vs EXECUTE IMMEDIATE - sql

Which are the differences between DBMS_UTILITY.EXEC_DDL_STATEMENT and EXECUTE IMMEDIATE?

Fundamentally they do the same thing, which is to provide a mechanism to execute DDL statements in PL/SQL, which isn't supported natively. If memory serves me well, the EXEC_DDL_STATEMENT was available in the Oracle 7 version of the DBMS_UTILITY package, whereas Native Dynamic SQL (EXECUTE IMMEDIATE) was only introduced in 8.
There are a couple of differences. EXECUTE IMMEDIATE is mainly about executing dynamic SQL (as its NDS alias indicates). the fact that we can use it for DDL is by-the-by. Whereas EXEC_DDL_STATEMENT() - as the suggests - can only execute DDL.
But the DBMS_UTILITY version isn't retained just for backwards compatibility, it has one neat trick we cannot do with EXECUTE IMMEDIATE - running DDL in a distributed fashion. We can run this statement from our local database to create a table on a remote database (providing our user has the necessary privileges there):
SQL> exec DBMS_UTILITY.EXEC_DDL_STATEMENT#remote_db('create table t1 (id number)');
I'm not recommending this, just saying it can be done.

I realize I am 9 years late to the reply but there is one additional difference.
dbms_utility.exec_ddl_statement will not execute anything but DDL. If you try to do say an insert, it will not do it. It will also not return an error either so you won't know that you did not insert.
-- drop table kevtemp1;
create table kevtemp1 (a integer);
insert into kevtemp1 values (1);
commit;
begin
insert into kevtemp1 values (2);
end;
/
commit;
begin
DBMS_UTILITY.EXEC_DDL_STATEMENT('insert into kevtemp1 values (3)');
end;
/
commit;
select * from kevtemp1;

Related

Postgres: Save rows from temp table before rollback

I have a main procedure (p_proc_a) in which I create a temp table for logging (tmp_log). In the main procedure I call some other procedures (p_proc_b, p_proc_c). In each of these procedures I insert data into table tmp_log.
How do I save rows from tmp_log into a physical table (log) in case of exception before rollback?
create procedure p_proc_a
language plpgsql
as $body$
begin
create temp table tmp_log (log_message text) on commit drop;
call p_proc_b();
call p_proc_c();
insert into log (log_message)
select log_message from tmp_log;
exception
when others then
begin
get stacked diagnostics
v_message_text = message_text;
insert into log (log_message)
values(v_message_text);
end;
end;
$body$
What is a workround to save logs into a table and rollback changes from p_proc_b and p_proc_c?
That is not possible in PostgreSQL.
The typical workaround is to use dblink to connect to the database itself and write the logs via dblink.
I found three solutions to store data within a transaction (im my case - for debugging propose), and still be able to see that data after rollback-ing the transaction
I have a scenario where inside, I use following block, so it may not apply to your scenario
DO $$
BEGIN
...
ROLLBACK;
END;
$$;
Two first solutions are suggested to me in the Postgres slack, and the other I tried and found after talking with them, a way that worked in other db.
Solutions
1 - Using DBLink
I don't remember how it was done, but you import some libraries and then connect to another db, and use the other DB - which maybe can support to be this db - which seem to be not affected by transactions
2 - Using COPY command
Using the
COPY (SELECT ...) TO PROGRAM 'psql -c "COPY xyz FROM stdin"'
BTW I never used it, and it seems that it requires Super User(SU) permission in Unix. And god knows how it is used, or how it output data
3 - Using Sub-Transactions
In this way, you use a sub-transaction (which I'm not sure it it's correct, but it must be called Autonomous transactions) to commit the result you want to keep.
In my case the command looks like this:
I used a Temp Table, but it seems (I'm not sure) to work with an actual table as well
CREATE TEMP TABLE
IF NOT EXISTS myZone AS
SELECT * from public."Zone"
LIMIT 0;
DO $$
BEGIN
INSERT INTO public."Zone" (...)VALUES(...);
BEGIN
INSERT INTO myZone
SELECT * from public."Zone";
commit;
END;
Rollback;
END; $$;
SELECT * FROM myZone;
DROP TABLE myZone;
don't ask what is the purpose of doing this, I'm creating a test scenario, and I wished to track what I did until now. since this block did not support SELECT of DQL, I had to do something else, and I wanted a clean set of report, not raising errors
According to www.techtarget.com:
*Autonomous transactions allow a single transaction to be subdivided into multiple commit/rollback transactions, each of which
will be tracked for auditing purposes. When an autonomous transaction
is called, the original transaction (calling transaction) is
temporarily suspended.
(This text was indexed by google and existed on 2022-10-11, and the website was not opened due to an E-mail validation issue)
Also this name seems to be coming from Oracle, which this article can relate
EIDTED:
Removing solution 3 as it won't work
POSTGRES 11 Claim to support Autonomous Transactions but it's not what we may expect...
For this functionality Postgres introduced the SAVEPOINT:
SAVEPOINT <name of savepoint>;
<... CODE ...>
<RELEASE|ROLLBACK> SAVEPOINT <name of savepoint>;
Now the issue is:
If you use nested BEGIN, the COMMIT Inside the nested code can COMMIT everything, and the ROLLBACK in the outside block will do none (Not rolling back anything that happened before COMMIT of inner
If you use SAVEPOINT, it is only used to rollbacks part of the code, and even if you COMMIT it, the ROLLBACK in the outside block will rollback the SAVEPOINT too

Why is my initial SQL (Oracle) not accepted by Tableau?

For a project I've been working on, I had to create 2 tables in Oracle using Tableau's Initial SQL window. I basically need to re-create them each time Tableau does an extract, so would have to drop and re-create them. Only using the 'DROP' statement works, but if the extract fails in the middle of it, then when it re-runs, the tables don't exist, thus it returns an error.
I tried to use the below code which works fine in SQL Developer, but Tableau doesn't seem to accept it.
--Searches and deletes table TABLEAU_LCC_LEAD_TIME if it exists
DECLARE
does_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT (does_not_exist, -942);
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE TABLEAU_DOC_LEAD_TIMES';
EXCEPTION
WHEN does_not_exist
THEN
NULL;
END;
/
Tableau returns this error
Would you know of any workaround for the "/" in Tableau Initial-SQL for Oracle?
I think use temporary table in Oracle. Not need re-created table. https://oracle-base.com/articles/misc/temporary-tables

Does PL/SQL Procedure Automatically Commit When it Exits? [duplicate]

I have 3 tables in oracle DB. I am writing one procedure to delete some rows in all the 3 tables based on some conditions.
I have used all three delete statements one by one in the procedure. While executing the mentioned stored procedure, is there any auto-commit happening in the at the time of execution?
Otherwise, Should I need to manually code the commit at the end?
There is no auto-commit on the database level, but the API that you use could potentially have auto-commit functionality. From Tom Kyte.
That said, I would like to add:
Unless you are doing an autonomous transaction, you should stay away from committing directly in the procedure: From Tom Kyte.
Excerpt:
I wish PLSQL didn't support commit/rollback. I firmly believe
transaction control MUST be done at the topmost, invoker level. That
is the only way you can take these N stored procedures and tie them
together in a transaction.
In addition, it should also be noted that for DDL (doesn't sound like you are doing any DDL in your procedure, based on your question, but just listing this as a potential gotcha), Oracle adds an implicit commit before and after the DDL.
There's no autocommit, but it's possible to set commit command into stored procedure.
Example #1: no commit
create procedure my_proc as
begin
insert into t1(col1) values(1);
end;
when you execute the procedure you need call commit
begin
my_proc;
commit;
end;
Example #2: commit
create procedure my_proc as
begin
insert into t1(col1) values(1);
commit;
end;
When you execute the procedure you don't nee call commit because procedure does this
begin
my_proc;
end;
There is no autocommit with in the scope of stored procedure. However if you are using SQL Plus or SQL Developer, depending on the settings autocommit is possible.
You should handle commit and rollback as part of the stored procedure code.

Dynamic SQL - Check syntax and semantics

With Oracle dynamic SQL one is able to execute a string containing a SQL statement. e.g.
l_stmt := 'select count(*) from tab1';
execute immediate l_stmt;
Is it possible to not execute l_stmt but check that the syntax and semantics is correct programmitically?
EXPLAIN PLAN will check the syntax and semantics of almost all types of SQL statements. And unlike DBMS_SQL.PARSE it will not implicitly execute anything.
The point of the explain plan is to show how Oracle will execute a statement. As a side-effect of generating the plan it must also check syntax, privileges, and generally do everything except actually run the statement. The explain plan itself is pointless and can be ignored, the statement is only run to check for any errors. As long as there are no errors, the statement is valid.
For example, the PL/SQL blocks below check the validity of a SELECT statement and a CREATE TABLE statement. They run without error so the syntax is fine.
begin
execute immediate 'explain plan for select * from dual';
end;
/
begin
execute immediate 'explain plan for create table just_some_table(a number)';
end;
/
Running a bad statement will generate an error. In at least this one test case, it generates the same error as if the statement was run by itself.
begin
execute immediate 'explain plan for select * from this_table_does_not_exist';
end;
/
ORA-00942: table or view does not exist
ORA-06512: at line 2
The syntax diagram in the manual implies it should run for all statements. However, there appear to be at least a few statement types that do not work, such as ALTER SESSION.
begin
execute immediate 'explain plan for alter session set optimizer_features_enable = ''11.2.0.4''';
end;
/
ORA-00900: invalid SQL statement
ORA-06512: at line 2
Slightly off-topic - are you trying to build a completely generic SQL interface, like a private SQL Fiddle built in PL/SQL? Do you need to worry about things like preventing users from attempting to run certain statement types, and ensuring there are no trailing semicolons? If so I can edit the question to help with some of those difficult dynamic SQL tasks.
I think that the only "solution" is to use DBMS_SQL.PARSE().
It is not perfect but it is the best that you can get
Hope this way you can check the query formed before execution.
set serveroutput on;
DECLARE
lv_sql VARCHAR2(1000);
lv_cnt PLS_INTEGER;
BEGIN
lv_sql:='select count(*) from tab1';
dbms_output.put_line(lv_sql);
--EXECUTE IMMEDIATE lv_sql INTO lv_cnt;
END

ORACLE Batching DDL statements within a Execute Immediate

I'm trying to run multiple ddl statements within one Execute Immediate statement.
i thought this would be pretty straight forward but it seems i'm mistaken.
The idea is this:
declare v_cnt number;
begin
select count(*) into v_cnt from all_tables where table_name='TABLE1' and owner = 'AMS';
if v_cnt = 0 then
execute immediate 'CREATE TABLE TABLE1(VALUE VARCHAR2(50) NOT NULL) ALTER TABLE TABLE1 ADD (MYVAL2 NVARCHAR2(10))';
end if;
end;
however this results in an error
ORA-00911: invalid character
ORA-06512: at line 10
Each of the statements within the batch run fine if i execute them by themselves. and if i take this statement and execute it, it will run fine (with the ; between the 2 statements). If i remove the ; between statements i get a different error about invalid option
the plan is that i'll be able to build a table, export the table schema for this table including all it's alter statements, and then run the batch against another system as part of an install/update process.
So, how do i batch these DDL statements within a single execute immediate? Or is there a better way to do what i'm needing?
I'm a bit of a Oracle newb, i must admit. Thank you all for your patience.
The semicolon is not part of Oracle's SQL syntax. SQL*Plus and other client side tools use semicolon to signal the end of a SQL Statement, but the server doesn't see it.
We can force SQL*Plus to pass the semicolon to the DB:
SQL> set sqlterminator off
SQL> select * from user_tables;
2 /
select * from user_tables;
*
ERROR at line 1:
ORA-00911: invalid character
If i take this statement and execute it, it will run fine (with the ; between the 2 statements) The client tool you are using, breaks it into two calls to the DB.
So, I don't think it is possible to pass multiple statements inside an execute immediate.
I suppose one could call execute immediate with a string containing an anonymous PL/SQL block, with individual calls to execute immediate inside it ... and I don't know what the point of doing that would be. ;)
Why do you need a single EXECUTE IMMEDIATE call? Surely just do it as 2 calls?
Bear in mind that each DDL statement contains an implicit COMMIT, so there's no concurency benefit to doing it as a single call.
Also, why not just set up the table correctly in the first call? You could do...
CREATE TABLE TABLE1(VALUE VARCHAR2(50) NOT NULL, MYVAL2 NVARCHAR2(10))
...instead of needing 2 calls.
Also, have you looked at DBMS_METADATA... it can generate DDL for objects such as tables for you.