Skip to Main Content
  • Questions
  • Execute multiple DDL statements at once

Breadcrumb

Question and Answer

Chris Saxon

Thanks for the question, Norman.

Asked: September 12, 2016 - 12:21 pm UTC

Last updated: September 14, 2016 - 12:52 am UTC

Version: 11.2.0.4

Viewed 10K+ times! This question is

You Asked

Hi Tom,

I need your expertise in this regard.
I´m using a self programmed version control system to upgrade my customers systems.
In it all the changeset with packages, functions, DDL commands, DML commands and all this are saved in BLOBs in the database.

To Update my customers all these files and commands are written in a big sql-file which containt everything.
There are create statements, inserts, updates, alter table,...and all this in it.

As a next step i´m executing this file in a SQL-plus on the customers system.
It works fine but i´m looking for a better way with more control and logging.

So i´m testing how i can execute multiple ddl statements and all these sources dynamically.

I´m giving some examples the sources are stored:

My dbupdaes are stored like this for example
(as one file stored in a blob with multiple ddl statements):
-- Create table
create table T605SCFG
(
  betart         NUMBER(6) not null,
  core_lang      VARCHAR2(4) default 'DE' not null,
  hostname       VARCHAR2(100) not null,
  portnumber     NUMBER(6) default 17000 not null,
  core_name      VARCHAR2(30) not null,
  core_repl_name VARCHAR2(40) not null
);

-- Add comments to the table 
comment on table T605SCFG
  is 'Konfiguration für SOLR';
-- Add comments to the columns 
comment on column T605SCFG.betart
  is 'Mandantnummer des Artikelstamms';

-- Create/Recreate primary, unique and foreign key constraints 
alter table T605SCFG
  add constraint T605SCFG_PK primary key (BETART, CORE_LANG);

alter table T605SCFG
  add constraint T605SCFG_UX1 unique (CORE_NAME);


It´s the same for functions, packages, procedures,...

One more example für a package how it´s stored:

create or replace package shc_ab003_objects is
  
  -- public functions and procedures

  -- einen Positionslauf zurückgeben (auf T087poli basierend)
  function get_t_ab003_poli(pi_manr number
                           ,pi_ainr number
                           ,pi_aufpos number
                           ,pi_lauf number
                           ) return t_ab003_poli;
.
.
.
.
.
.

end shc_ab003_objects;
/
create or replace package body shc_ab003_objects is

  -- Cursor-Getter für T_AB003_POLI
  procedure get_t_ab003_poli_cursor(po_cursor out sys_refcursor
                                   ,pi_manr in number
                                   ,pi_ainr in number
                                   ,pi_aufpos in number
                                   ,pi_lauf in number default 0
                                   ) is
  v_lauf number := nvl(pi_lauf, 0);
  begin
  open po_cursor for
     select t_ab003_poli(t1.manr, t1.ainr, t1.lauf, t1.aufpos, t1.menge
                        ,t1.stat, t1.fstat, t1.diffvw, t1.diffew, t1.vkpr         
                        ,t1.eipr, t1.raba, t1.arnr, t1.kdnr, t1.fr5
                        ,decode(t2.mwco, null, 0, t2.mwco), t1.baspr
                        )
       from t087poli t1
       left outer join t387poli_zusi t2 on (t2.manr = t1.manr and t2.ainr = t1.ainr and t2.aufpos = t1.aufpos and t2.lauf = t1.lauf)
      where 1=1
        and t1.manr = pi_manr
        and t1.ainr = pi_ainr
        and t1.aufpos = pi_aufpos
        and (v_lauf = 0 or v_lauf = t1.lauf)
      order by t1.lauf
      ;    
  end;
  
.
.
.
.
.
.
.
  
begin
  -- Initialisierung
  null;
  
end shc_ab003_objects;
/




Now i´m looking for a way to execute them in the same structure they´re stored.

Maybe there is a way with execute immediate or a dbms-package or anything else which i don´t know...

Big Thanks for your help!

and Chris said...

So... you want to store your DDL in tables. Then have self-creating scripts to build your installation from these?

Please. Don't do this!

It's tricky to implement. But more importantly it's a security nightmare! Ensuring that people only run valid changes and not something nefarious is hard.

Just use regular text files to store your DDL changes. Then use git or SVN to source control these. If you're using SQL Developer, this has build-in support for them!

http://www.thatjeffsmith.com/archive/2014/06/managing-scripts-in-oracle-sql-developer/
http://www.thatjeffsmith.com/archive/2012/06/do-you-develop-your-plsql-directly-in-the-database/

There are various solutions that can help you take your source files and turn them installation scripts.

I've no real experience of these, though I hear good reports about Liquibase:

http://www.liquibase.org/

Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Thanks for the answer Chris, but it´s no option don´t store it like this way

Norman Hisgen, September 12, 2016 - 2:30 pm UTC

Hi Chris,

thanks for your fast answer.
Let me get you some more information.

All the DDL changes are stored in textfiles on the filesystem.
Everyone is compiled in a development environment, so everyone will be a valid and compileable change.

I´ve got a self developed release system like git or svn, every change is checked in and stored in a blob after it´s checked in. The application which all our developers operate checks this also, when the changes gets checked in.

Our developed business application is released to lots of customers with different release states. We save which change is allready rolled out to them and which one isn´t.
They can request a new release independently, then the system is creating the whole changeset and transfer it to the customers.

It´s still working fine, since about 14years.

How ever, the installation script isn´t my problem.
I´m just looking for a comfortable way to run the installation script directly on the database without sql-plus.
I think there must be a way, cause sql plus does it also with multiple ddl at once.

Maybe you can give me some tips or tricks.

The background is that some of the commands or packages can´t get compiled cause the tables or packages are locked of inactive sessions. These sessions are from webservers and reportservers and all like this.
If i could run the changes directly in the database i could log it on a much better way und reimport the logfiles to my own system for analyses.

We aren´t working with the SQL Developer also, we´re using the PLSQL Developer of Allround Automations, maybe u know.
Chris Saxon
September 12, 2016 - 4:27 pm UTC

If you need to run scripts in the database without SQL*Plus, why not submit a job?

You can build an execute immediate statement for each DDL command you have. See the post from Duke below for an example of doing this.

Tricky indeed

Duke Ganote, September 12, 2016 - 3:36 pm UTC

Yes, it is tricky to implement, but architectures can be inflexible, tools can be difficult to get approval for, etc. etc. So I've had to rely on DIY magic at times.

SQL> create table ddl_stuff ( order# integer primary key, ddl_stmt clob );

Table created.

SQL> insert into ddl_stuff values ( 1, 'CREATE TABLE XXX ( XXX integer )' );

1 row created.

SQL> ed
Wrote file c:/users/DGANOTE/sqlplusedit.sql

  1  insert into ddl_stuff values ( 2, q'<BEGIN
  2       FOR rec IN ( SELECT count(*) cnt
  3                      FROM USER_TAB_COLUMNS
  4                     WHERE TABLE_NAME = 'XXX'
  5                       AND COLUMN_NAME = 'COLUMNINDEX'
  6                  ) LOOP
  7         CASE WHEN rec.cnt = 0
  8              THEN
  9               EXECUTE IMMEDIATE 'ALTER TABLE XXX ADD COLUMNINDEX INTEGER DEFAULT 0 NOT NULL';
 10          END CASE;
 11       END LOOP;
 12*    END;>' )
SQL> /

1 row created.

SQL> select * from ddl_stuff;

    ORDER# DDL_STMT
---------- --------------------------------------------------------------------------------
         1 CREATE TABLE XXX ( XXX integer )
         2 BEGIN
                FOR rec IN ( SELECT count(*) cnt
                               FROM USER_TAB_COLUMNS
                              WHERE TABLE_NAME = 'XXX'
                                AND COLUMN_NAME = 'COLUMNINDEX'
                           ) LOOP
                  CASE WHEN rec.cnt = 0
                       THEN
                        EXECUTE IMMEDIATE 'ALTER TABLE XXX ADD COLUMNINDEX INTEGER DEFAULT
           0 NOT NULL';
                   END CASE;
                END LOOP;
              END;


SQL> ed
Wrote file c:/users/DGANOTE/sqlplusedit.sql

  1  BEGIN for REC in ( SELECT ddl_stmt FROM ddl_stuff order by order# ) LOOP
  2   EXECUTE IMMEDIATE rec.ddl_stmt;
  3* END LOOP; END;
SQL> /

PL/SQL procedure successfully completed.

SQL> desc xxx
 Name                            Null?      Type
 ---------------------------- -------- ----------
 XXX                                   NUMBER(38)
 COLUMNINDEX                  NOT NULL NUMBER(38)

SQL>


SQL*Developer is handy for editing the CLOB contents. And of course there's lots of testing involved because you are "playing trapeze artist without a safety net"!
Chris Saxon
September 12, 2016 - 4:29 pm UTC

Nice example, thanks.

"playing trapeze artist without a safety net"!

Indeed!

Execute multiple DDL statements at once, not separately

Norman Hisgen, September 13, 2016 - 3:13 pm UTC

Hi,

i´m not sure you´ve understood my question, cause my english isn´t the best....

I know that i can run every ddl separately with a execute immediate, but that´s exactly the problem.
I´ve got more than one ddl in each file respectively in one blob. If i would have to run everyone seperately i would have to splitt the different ddl commands before i can execute them with an execute immediate.

Thats the reason why i´m looking towards a way to run them in one action. to run multiple ddl statements at once without to split them.

Chris Saxon
September 13, 2016 - 4:37 pm UTC

In the general case you can't.

It is possible to create multiple tables, views and grants in a single statement. To do this you need to wrap them in a "create schema" command:

https://blogs.oracle.com/sql/entry/creating_multiple_tables_in_a

But when it comes to everything else (altering tables, indexes, PL/SQL, etc.) you need to split and execute them separately.

Splits and dependencies

Duke Ganote, September 13, 2016 - 8:42 pm UTC

So there are two separate issues:
1. Multiple DDL statements in a file/CLOB need to be separated. Certainly SQL*PLUS has built-in parsing to do so for a file. In the database, hmm.
2. Parallelizing statements that are serially executed in the file/CLOB. There are dependencies between statements. Those would have to be determined in order to efficiently run parallel streams.

Chris Saxon
September 14, 2016 - 12:52 am UTC

yup.... and #2 in particular doesnt sound like fun

More to Explore

CLOB/BLOB/etc

Complete documentation on Securefiles and Large Objects here